Photo by Jayphen秋山です。
先日「Python Fire」という、Pythonのコマンドラインツールを自動生成できるライブラリが発表されました。
どのへんが便利なのか、実際に使ってみながら解説をしていきますので、気になってた人の参考になればと思います。
Googleのリポジトリに出ているのでGoogleの公式プロダクト?と思いきや、最後に「This is not an official Google product.」の表記があるのでGoogle公式ではないようですね。Googleの中の人が作った非公式ライブラリということでしょうかね。
■Python Fire使ってみた
そもそもPythonには、コマンドラインからコマンドを受け付ける組み込みのライブラリがあります。他にもbakerとか、clickとか、既にコマンドラインツールを作る用の補助ライブラリもいくつかあります。
今回は「コマンドを叩けば流行りのbitcoinの価格をチェックできるようなツールを作る」ことを目的に、Python Fireと各ライブラリを試しながら比較してみたいと思います。(ちなみに私は普段はよくbakerか組み込みライブラリを使っています)
bitFlyerさんが公開されている、簡単に使えるtickerを取得するpublic apiを利用させていただきます。(詳しくはこちら)
ちなみにbitFlyerさんの求人情報はこちら。
株式会社bitFlyer エンジニア求人 | ITプログラマ・エンジニア向け転職・就活・学習サービスのpaiza
ではまず単純に、価格を取得するpythonのコードを書いてみます。
defbitflyer_getticker(product_code, is_json): end_point = "https://api.bitflyer.jp/v1/getticker" r = requests.get(end_point, params={'product_code' : product_code}) r_json = json.loads(r.text) if product_code == None: print('BTC_JPY') else: print(product_code) if is_json: print(r_json) else: print("bid:{}".format(r_json['best_bid'])) print("ask{}".format(r_json['best_ask']))
product_codeは、BTC_JPY(bitcoinと円) やBTC_ETC(bitcoinとetherium)を指定することができます。(詳細はbitFlyerさんのAPIのドキュメントを参照してください)
is_jsonがtrueなら、jsonをprintするようにしています。これ以降はこのメソッドを流用して、各ライブラリでコマンドラインツール化をしてみましょう。
コードの再利用性や他のAPIへの対応はひとまず考えずに、tickerの取得のみをします。
まずはPython標準の argparse を使ってみましょう。
defbitflyer_getticker(product_code, is_json): end_point = "https://api.bitflyer.jp/v1/getticker" r = requests.get(end_point, params={'product_code' : product_code}) r_json = json.loads(r.text) if product_code == None: print('BTC_JPY') else: print(product_code) if is_json: print(r_json) else: print("bid:{}".format(r_json['best_bid'])) print("ask{}".format(r_json['best_ask'])) import argparse parser = argparse.ArgumentParser(description='bitflyer api getticker') parser.add_argument('-p', '--p', type=str, help=u'BTC_JPY or BTC_ETH') parser.add_argument('-j', '--json', action='store_true', help=u"return json") args = parser.parse_args() bitflyer_getticker(product_code=args.p, is_json=args.json)
上記のコードでは、-pオプションでBTC_JPYとかETH_BTCを文字列で指定できるようにして、--jsonオプションでjsonを返すようにしてみました。
python bitflyer.py -p ETH_BTC --json
とすると、ETH_BTCを指定して、jsonを出力となります。各コマンドを省略するとBTC_JPYのaskとbidのみ出力します。
とりあえず、使えるようにはなりましたね。
では次に、bakerを使ってみましょう。
bakerはimportして@baker.commandするだけで、なんとな~くコマンドラインツールっぽくしてくれます。
import requests, json, baker @baker.command(params={"product_code": "'BTC_JPY or BTC_ETH", "is_json": "return json"}) defbitflyer_getticker(product_code='BTC_JPY', is_json=False): """bitflyer api getticker """ end_point = "https://api.bitflyer.jp/v1/getticker" r = requests.get(end_point, params={'product_code' : product_code}) r_json = json.loads(r.text) if product_code == None: print('BTC_JPY') else: print(product_code) if is_json: print(r_json) else: print("bid:{}".format(r_json['best_bid'])) print("ask{}".format(r_json['best_ask'])) baker.run()
これで、メソッド名をサブコマンドとしてコマンドラインツールっぽく使えるようになります。
python bitflyer_baker.py bitflyer_getticker --product_code ETH_BTC --is_json
ETH_BTCを指定し、jsonを出力します。各コマンドを省略すると、BTC_JPYのaskとbidのみ出力します。
メソッド名がサブコマンドとされたり、–helpとするとコメントの部分が出力されたり、paramsで設定したオプションの詳細などが以下のように表示されます。
python bitflyer_baker.py bitflyer_getticker --help Usage: bitflyer_baker.py bitflyer_getticker [<product_code>] [<is_json>] bitflyer api getticker Options: --product_code 'BTC_JPY or BTC_ETH --is_json return json
ざっくりコマンドツールを作る場合はこれで十分かもしれません。
続いてclickでやってみます。
import requests, json, click @click.command() @click.option('--product_code', '-p', default='BTC_JPY', help=u'BTC_JPY or BTC_ETH') @click.option('--is_json/--no-is_json', default=False, help=u"return json") defbitflyer_getticker(product_code='BTC_JPY', is_json=False): end_point = "https://api.bitflyer.jp/v1/getticker" r = requests.get(end_point, params={'product_code' : product_code}) r_json = json.loads(r.text) if product_code == None: print('BTC_JPY') else: print(product_code) if is_json: print(r_json) else: print("bid:{}".format(r_json['best_bid'])) print("ask{}".format(r_json['best_ask'])) if __name__ == "__main__": bitflyer_getticker()
python bitflyer_click.py -p ETH_BTC --is_json
とすると、ETH_BTCを指定しjsonを出力となります。各コマンドを省略するとBTC_JPYのaskとbidのみ出力します。
こちらも–helpオプションで表示されるメッセージが、オプションなどからいい感じに作ってもらえます。こんな感じで表示されます。
python bitflyer_click.py --help Usage: bitflyer_click.py [OPTIONS] Options: -p, --product_code TEXT BTC_JPY or BTC_ETH --is_json / --no-is_json return json --help Show this message and exit.
では最後に、Python Fireで作ってみます。
import requests, json, fire defbitflyer_getticker(product_code='BTC_JPY', is_json=False): end_point = "https://api.bitflyer.jp/v1/getticker" r = requests.get(end_point, params={'product_code' : product_code}) r_json = json.loads(r.text) if product_code == None: print('BTC_JPY') else: print(product_code) if is_json: print(r_json) else: print("bid:{}".format(r_json['best_bid'])) print("ask{}".format(r_json['best_ask'])) if __name__ == "__main__": fire.Fire(bitflyer_getticker)
ちなみにクラスをつっこむなら以下のようにします。
import requests, json, fire classBitflyer(): def__init__(self, end_point="https://api.bitflyer.jp/v1/getticker"): self.end_point = end_point defgetticker(self, product_code='BTC_JPY', is_json=False): r = requests.get(self.end_point, params={'product_code' : product_code}) r_json = json.loads(r.text) if product_code == None: print('BTC_JPY') else: print(product_code) if is_json: print(r_json) else: print("bid:{}".format(r_json['best_bid'])) print("ask{}".format(r_json['best_ask'])) if __name__ == "__main__": fire.Fire(Bitflyer)
fire.Fire()にクラスを投げ込むだけでコマンドラインツール化してくれます。
コマンド上では、クラスを突っ込んだ場合はメソッド名をサブコマンドとして、このように引数を順に与えている感じになります。
python bitflyer_fire.py getticker ETH_BTC True
引数の順番を変えたい場合などは -- をつけて
python bitflyer_fire.py getticker --product_code=ETH_BTC --is_json=True
というようなコマンドで実行できます。
ヘルプは以下のようになります。
python bitflyer_fire.py -- --help Type: type String form: <class'__main__.Bitflyer'> File: ~/python_commands/bitflyer_fire.py Line: 4 Usage: bitflyer_fire.py [END_POINT] bitflyer_fire.py [--end-point END_POINT]
さらにPython Fire用の特殊なオプションがいくつかあります。
それらオプションは上記で説明したコマンドの末尾に -- で区切り --verbose などとつけることが出来ます。
上記のクラスをfireに渡した場合を例に試してみます。
--interactive
こちらはインタラクティブモードで実行、IPython上で実行されます。
python bitflyer_fire.py -- --interactive
とすると、以下のようにREPLが起動します。
Fire is starting a Python REPL with the following objects: Modules: fire, json, requests Objects: Bitflyer, bitflyer_fire.py, component, result, trace Python 3.6.0 (default, Jan 52017, 13:12:02) Type "copyright", "credits"or"license"for more information. IPython 5.1.0 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features.%quickref -> Quick reference.help -> Python's own help system. object? -> Details about 'object', use 'object??'for extra details. In [1]: Bitflyer().getticker() BTC_JPY bid:138251.0 ask138407.0 In [2]:
REPL上でBitflyer().getticker()とするとコマンド実行と同じように動作します。
--completion
こちらのコマンドはbash用のコマンドの補完が出来るようになるスクリプトを吐きます。
.bashrcなどに貼り付け、パスが通っており、実行権限を与えると動作するようになります。
他にも以下のようなオプションがいくつかありますが、特に有用なのは上2つかと思います。
--help
--trace
--separator
--verbose
ちなみにこれらオプションの詳細は、以下のページに記載されています。
python-fire/using-cli.md at master · google/python-fire · GitHub
■まとめ
今の目的だと、Pythonデフォルト以外は割とどれも似たような感じですね。
- bakerは、メソッドをつっこむだけで引数などから勝手にヘルプを作ってもらえます。何も考えずに作れるなら個人的にはこれが好きです。
- clickは、デコレータでoptionなどを設定し、そこからヘルプを作ります。少し面倒ですが、 --is_json/--no-is_jsonといった boolean を --no〜 とかで False を設定できるようにしたりと、柔軟な設定が可能です。
- Python Fireは、クラスやメソッドを投げ込む感じなので、既存のものをコマンド化するという点ではbakerに近いのかなと思います。ヘルプなどの自動生成はbakerに似てますね。
それぞれ色々な機能がありそうですが今回試した感じでは、何も考えずに既存のクラスやメソッドを入れたい!みたいな使い方をするのであれば、Python Fireは良さそうですね。
help上に String form:
またbash用のスクリプトを吐く、インタラクティブモードがあるなど、便利機能も組み込まれているので上手く使えれば捗りそうです。
■Pythonの講座も公開中!プログラミングが動画で学べるレッスン
paizaは、プログラミング未経験者・初心者向け学習サービス「paizaラーニング」を、新サービスとして独立オープンいたしました。
今回記事の中で使用しているPythonの入門講座も好評公開中です。ぜひごらんください!
そして、paizaでは、Webサービス開発企業などで求められるコーディング力や、テストケースを想定する力などが問われるプログラミングスキルチェック問題も提供しています。
paiza.jp
スキルチェックに挑戦した人は、その結果によってS・A・B・C・D・Eの6段階のランクを取得できます。必要なスキルランクを取得すれば、書類選考なしで企業の求人に応募することも可能です。「自分のプログラミングスキルを客観的に知りたい」「スキルを使って転職したい」という方は、ぜひチャレンジしてみてください。