Python add_subparsers()とは?argparseでサブコマンドを作る方法
Pythonのadd_subparsers()は、gitやdockerのような「サブコマンド」を作る機能です。
tool add・tool delのように、1つのコマンドで複数の機能を提供できます。
add_subparsers()の基本的な使い方
import argparse
p = argparse.ArgumentParser(prog="tool")
sp = p.add_subparsers(dest="cmd", required=True)
a = sp.add_parser("add", help="追加する")
a.add_argument("name")
d = sp.add_parser("del", help="削除する")
d.add_argument("id", type=int)
d.add_argument("--force", action="store_true")
args = p.parse_args()
print(args)
実行結果:
$ python sub.py add 佐藤
Namespace(cmd='add', name='佐藤')
$ python sub.py del 5 --force
Namespace(cmd='del', id=5, force=True)
手順は3ステップです。
add_subparsers()でサブコマンドの入れ物を作るadd_parser()でサブコマンドを1つ追加する- 各サブパーサーに
add_argument()で専用の引数を登録する
サブコマンドごとに違う引数を持てるのが最大の利点です。addは名前を、delはIDと--forceを受け取っています。
destとrequiredは必ず指定する
sp = p.add_subparsers(dest="cmd", required=True)
| 引数 | 役割 | 指定しないと |
|---|---|---|
dest |
どのサブコマンドか記録する | 判別できない |
required |
サブコマンドを必須にする | 何もせず終了する |
required=Trueがないと、サブコマンドなしで実行してもエラーにならず素通りします。
$ python sub.py
tool: error: the following arguments are required: cmd
指定しておけば、この親切なメッセージが出ます。
ヘルプが階層になる
$ python sub.py --help
...
add 追加する
del 削除する
optional arguments:
-h, --help show this help message and exit
全体のヘルプにはサブコマンドの一覧が出ます。
$ python sub.py add --help
usage: tool add [-h] name
positional arguments:
name
サブコマンドごとにも--helpが使えます。すべて自動生成で、1行も書いていません。
add_parser()のhelpが一覧に、descriptionが個別ヘルプの説明文になります。
set_defaults()で関数を紐づける
実務で最も使われるパターンです。
import argparse
def do_add(args):
print("追加:", args.name)
def do_del(args):
print("削除:", args.id)
p = argparse.ArgumentParser()
sp = p.add_subparsers(required=True)
a = sp.add_parser("add")
a.add_argument("name")
a.set_defaults(func=do_add)
d = sp.add_parser("del")
d.add_argument("id", type=int)
d.set_defaults(func=do_del)
args = p.parse_args()
args.func(args)
実行結果:
$ python sd.py add 鈴木
追加: 鈴木
$ python sd.py del 9
削除: 9
if args.cmd == "add": ... elif ...という分岐が不要になります。
サブコマンドを増やすときも、分岐を書き足す必要がありません。add_parser()とset_defaults()を1組追加するだけです。
共通のオプションを持たせる
「全サブコマンドで--verboseを使いたい」という場合、親に付けると位置が制限されます。
p.add_argument("--verbose", action="store_true")
sp = p.add_subparsers(dest="cmd", required=True)
この場合、サブコマンドより前に書く必要があります。
tool --verbose add 佐藤 # OK
tool add --verbose 佐藤 # エラー
どちらでも書けるようにするなら、親パーサーを使い回します。
import argparse
common = argparse.ArgumentParser(add_help=False)
common.add_argument("--verbose", action="store_true")
p = argparse.ArgumentParser()
sp = p.add_subparsers(dest="cmd", required=True)
a = sp.add_parser("add", parents=[common])
a.add_argument("name")
d = sp.add_parser("del", parents=[common])
d.add_argument("id", type=int)
print(p.parse_args(["add", "佐藤", "--verbose"]))
実行結果:
Namespace(cmd='add', verbose=True, name='佐藤')
add_help=Falseが必須です。付けないと-hが重複してArgumentErrorになります。
サブコマンドに別名を付ける
d = sp.add_parser("delete", aliases=["del", "rm"])
delete・del・rmのどれでも動きます。よく使うコマンドに短縮形を用意すると使い勝手が上がります。
ただしdestに入るのは打たれた名前そのままです。set_defaults()で関数を紐づけていれば、この違いを気にせずに済みます。
サブコマンドを使うべきか
| 状況 | 設計 |
|---|---|
| 機能が1つ | サブコマンド不要 |
| オプションで切り替えられる | --modeで十分 |
| 機能ごとに引数が違う | サブコマンド |
| 機能が3つ以上 | サブコマンド |
| 将来増える見込みがある | サブコマンド |
判断の軸は「引数の構成が機能ごとに違うか」です。
同じ引数で動作だけ変わるなら--modeのほうが簡単です。addは名前、delはID——のように受け取るものが違うならサブコマンドが適しています。
add_subparsers()のまとめ
add_subparsers()→add_parser()でサブコマンドを作るdestとrequired=Trueを必ず指定する- ヘルプは階層になって自動生成される
set_defaults(func=...)で分岐が不要になる- 共通オプションは
parents=[...]+add_help=False aliasesで別名を付けられる- 引数の構成が機能ごとに違うなら採用する






