はじめに
霞柱TUEEEEEEE!! nikkieです。
argparseでサブコマンドを実装する際の小ネタです。
目次
前回:argparseでサブコマンドを作る!
argparseはPython標準ライブラリの1つです。
先日、svn checkout
, svn update
, svn commit
のようなサブコマンドを実装する例を取り上げました。
ドキュメントで見つけた、サブコマンドをスッキリ実装するtips
https://docs.python.org/ja/3/library/argparse.html#argparse.ArgumentParser.add_subparsers にある例を元にした紹介です1。
サブコマンドを扱う1つの便利な方法は add_subparsers() メソッドと set_defaults() を組み合わせて、各サブパーサーにどの Python 関数を実行するかを教えることです。
ポイントはset_defaults
!
args
(parse_args
の返り値のNamespaceオブジェクト)を受け取る関数をサブコマンドの数だけ宣言します。
そしてその関数をset_defaults
でサブコマンドのパーサに設定するのです!
これによりサブコマンドのパーサに結び付けられた、args
を引数とする関数が実行されます。
分岐が標準ライブラリ側にラップされている感覚ですね。
この記事のコードの動作環境は Python 3.10.9 です。
% python subcommand_tips.py -h usage: subcommand_tips.py [-h] {foo,bar} ... positional arguments: {foo,bar} options: -h, --help show this help message and exit
サブコマンド動作例です
% python subcommand_tips.py foo 1 -x 2 2.0 % python subcommand_tips.py bar XYZYX ((XYZYX))
過去の私はサブコマンド名を格納して、分岐を実装していました
set_defaults
を知った今なら言える、「その必要はないわ」
サブコマンド名を格納して分岐を実装する例は以下です(ですがset_defaults
を使うのがオススメ!)
dest
引数を使ってサブコマンド名を格納します2
dest - サブコマンド名を格納する属性の名前です。デフォルトは None で値は格納されません (add_subparsersのドキュメントより)
% python subcommand_before.py foo 1 -x 2 2.0 % python subcommand_before.py bar XYZYX ((XYZYX))
終わりに
argparseを使ったサブコマンド実装で、set_defaults
でサブパーサに(Namespaceオブジェクトを引数とする)関数を設定するtipsを紹介しました。
サブコマンド名を格納して分岐を書いて、サブコマンドに応じて関数を呼ぶようなコードを書かなくてよくなります。
サブコマンドごとの処理の実行は、以下の1行だけになっちゃうんです。なんてスッキリ!
args.func(args)
私はPythonで実装するときに「エントリポイントを薄くする」3を信奉しています。
set_defaults
を使ったサブコマンド実装でエントリポイントが激薄になり、非常に好みだな〜と感じました。オススメです!
-
ドキュメントでは
add_subparsers
にrequired=True
を指定していますが、必要性が腹落ちしなかったので削除しています↩ - https://docs.python.org/ja/3/library/argparse.html#dest↩
-
rhoboroさんの「自信を持ってコードを書こう」より。https://docs.google.com/presentation/d/1qu3zFbzMh3AYhQ3DuDCDKbLlLqIrcklZdzi9fKyZPZQ/edit#slide=id.g6fea1dd43e_0_77 「
if __name__ == "__main__":
ブロック内もシンプルにする」↩