いろは2986023 views
小学算数1194618 views
中学理科1626207 views
雑学1472593 views
中学社会667106 views
高校国語785655 views
MathPython491378 views
英語607877 views
ヒストリア284143 views
LaTeX957300 views
Help
Tools

English

Python の argparse で相互排他オプションを設定する

コマンドラインツールでは、同時に指定してはいけないオプションの組み合わせがある。たとえば --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 行の括弧が角括弧 [] から丸括弧 () に変わり、必須であることが視覚的にもわかる。

required=False(デフォルト)

どちらも指定しなくてよい。usage 表示は [--json | --csv]

required=True

どちらか一方の指定が必須。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 つだけ」という設計に適している。

排他グループは引数の整合性をパーサーレベルで保証できるため、バリデーションコードを減らし、ヘルプメッセージでも制約を明示できる。オプションが増えてきたら、論理的にぶつかる組み合わせがないか確認し、積極的に活用するとよいだろう。