はじめに
霞柱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__":ブロック内もシンプルにする」↩