コマンドラインツールでは、同時に指定してはいけないオプションの組み合わせがある。たとえば --verbose と --quiet は意味が矛盾するし、--json と --csv のように出力形式を排他的に選ばせたい場面も多い。Python の argparse では add_mutually_exclusive_group() を使って、こうした相互排他のオプションを定義できる。
基本的な使い方
add_mutually_exclusive_group() はパーサーに対して呼び出し、返されたグループオブジェクトに対して add_argument() を追加していく。
import argparse parser = argparse.ArgumentParser(description="ログレベル設定") group = parser.add_mutually_exclusive_group() group.add_argument("--verbose", action="store_true", help="詳細出力") group.add_argument("--quiet", action="store_true", help="出力を抑制") args = parser.parse_args() if args.verbose: print("詳細モード") elif args.quiet: print("静粛モード") else: print("通常モード")
この定義により、--verbose と --quiet を同時に指定するとエラーが発生する。
$ python app.py --verbose 詳細モード $ python app.py --quiet 静粛モード $ python app.py --verbose --quiet usage: app.py [-h] [--verbose | --quiet] app.py: error: argument --quiet: not allowed with argument --verbose
エラーメッセージは argparse が自動生成してくれるため、自分でバリデーションを書く必要がない。ヘルプの usage 行でも [--verbose | --quiet] とパイプ記号で排他関係が示される。
required=True で必須にする
デフォルトでは、排他グループ内のどのオプションも指定しなくて構わない。しかし「どちらか一方は必ず選んでほしい」という場合もある。そのときは required=True を渡す。
import argparse parser = argparse.ArgumentParser(description="出力形式の指定") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--json", action="store_true", help="JSON 形式で出力") group.add_argument("--csv", action="store_true", help="CSV 形式で出力") args = parser.parse_args()
こうすると、どちらも指定しなかった場合にエラーになる。
$ python export.py usage: export.py [-h] (--json | --csv) export.py: error: one of the arguments --json --csv is required
usage 行の括弧が角括弧 [] から丸括弧 () に変わり、必須であることが視覚的にもわかる。
どちらも指定しなくてよい。usage 表示は [--json | --csv]
どちらか一方の指定が必須。usage 表示は (--json | --csv)
複数の排他グループ
1 つのパーサーに複数の排他グループを設けることもできる。出力形式とログレベルをそれぞれ独立した排他グループにする例を見てみよう。
import argparse parser = argparse.ArgumentParser(description="データ処理ツール") # 出力形式の排他グループ fmt_group = parser.add_mutually_exclusive_group() fmt_group.add_argument("--json", action="store_true", help="JSON 出力") fmt_group.add_argument("--csv", action="store_true", help="CSV 出力") # ログレベルの排他グループ log_group = parser.add_mutually_exclusive_group() log_group.add_argument("--verbose", action="store_true", help="詳細ログ") log_group.add_argument("--quiet", action="store_true", help="ログ抑制") args = parser.parse_args() print(args)
この場合、--json と --csv の間、--verbose と --quiet の間はそれぞれ排他だが、--json と --verbose のようにグループをまたいだ組み合わせは自由に指定できる。
$ python tool.py --json --verbose Namespace(json=True, csv=False, verbose=True, quiet=False) $ python tool.py --json --csv error: argument --csv: not allowed with argument --json
store_true 以外の排他オプション
排他グループは store_true 以外の引数でも使える。値を受け取るオプション同士を排他にするケースも珍しくない。
import argparse parser = argparse.ArgumentParser(description="認証方法の選択") auth_group = parser.add_mutually_exclusive_group(required=True) auth_group.add_argument("--token", type=str, help="APIトークンで認証") auth_group.add_argument("--user", type=str, help="ユーザー名で認証") args = parser.parse_args() if args.token: print(f"トークン認証: {args.token}") else: print(f"ユーザー認証: {args.user}")
--token と --user はどちらも文字列を受け取るが、同時には指定できない。認証方法のように「手段は複数あるが選べるのは 1 つだけ」という設計に適している。
排他グループは引数の整合性をパーサーレベルで保証できるため、バリデーションコードを減らし、ヘルプメッセージでも制約を明示できる。オプションが増えてきたら、論理的にぶつかる組み合わせがないか確認し、積極的に活用するとよいだろう。