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ステップです。

  1. add_subparsers()でサブコマンドの入れ物を作る
  2. add_parser()でサブコマンドを1つ追加する
  3. 各サブパーサーに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で別名を付けられる
  • 引数の構成が機能ごとに違うなら採用する

あわせて読みたい

対面・無料|東京・永田町で毎週土曜日13時

1回の参加で、プログラミングの構造がわかる

ゼロからはじめるPython入門講座

8年間・累計600回開催/これまで全国で2万人以上が参加

「やはり対面で聞けるのはとても気づきがあり、価値があると思います」

参加者の声

  • 本当に初心者からの講座です。プログラミングに触ったことがなくて大丈夫です
  • 対面だから、その場で聞けます。つまずいた所を持ち帰らずに解決できます
  • 講座は1時間。そのあとも残って学習を続けられ、講師にも質問できます
  • 独学で挫折した方も。糸口が見つかって、講座のあと会員になる方が多くいます

無料で参加する|開催日程を見る

参加費は無料です