Python が標準ライブラリとして提供している argparse モジュール を使用すると、複雑なコマンドライン引数を扱うスクリプトをシンプルに実装することができます。
単純に sys.argv
を使ってコマンドライン引数を扱う方法と比べ、argparse
を使うと次のような利点があります。
- Linux スタイルの短いオプション (
-o
) や長いオプション (--output
) を簡単に定義できます - ユーザーが入力した値を直感的なプロパティとして参照できます(例:
args.output
)。 - ヘルプメッセージを自動で構築してくれます(
-h (--help)
オプションはデフォルトで生成してくれます) - 各オプションの型を定義できます(数値型オプションに文字列が指定されたときに自動でエラーにしてくれます)
- サブコマンドを簡単に実装できます(例:
sample.py <サブコマンド> <引数1> <引数2>
)
ArgumentParser の基本
argparse
モジュールは、次のような流れで使用します。
ArgumentParser
クラスのインスタンスを生成するadd_argument
メソッドでコマンドライン引数の定義を追加していくparse_args
メソッドでパースしてNamespace
オブジェクトを取得するNamespace
オジェクトのプロパティでユーザー入力を参照する
次のスクリプトでは、-i (--input)
オプションと、-o (--output)
オプションを定義しています。
ヘルプ表示用のオプション -h (--help)
は自動で生成してくれます。
ユーザーが間違った使い方をしたときは、Usage を表示して終了してくれます。
$ python main.py hoge
usage: main.py [-h] [-i INPUT] [-o OUTPUT]
main.py: error: unrecognized arguments: hoge
$ python main.py -i
usage: main.py [-h] [-i INPUT] [-o OUTPUT]
main.py: error: argument -i/--input: expected one argument
いろいろなコマンドライン引数を定義する
ArgumentParser#add_argument
メソッドの呼び出し方によって、様々な仕様のコマンドライン引数を定義することができます。
位置引数 (positional argument)
parser.add_argument("name", type=str, help="your name")
parser.add_argument("age", type=int, help="your age")
# ...
print(f"name={args.name}, age={args.age}")
$ python main.py Maku 14
name=Maku, age=14
順序通り指定しなければいけない単純なコマンドライン引数です。
引数を -
や --
で始まらない名前にすると、この位置引数 (positional argument) として扱われます。
位置引数はデフォルトでユーザーによる入力が必須となりますが、nargs="?"
指定によりオプショナル扱いとなり、さらに default=...
指定によりデフォルト値を設定できます。
デフォルト値は、help
パラメーターの中で、%(default)s
で参照できます。
parser.add_argument(
"output", nargs="?", default="out.txt",
help="output file (default: %(default)s)"
)
必須の引数 (required=True)
parser.add_argument("-i", "--input", help="input file", required=True)
# ...
print(f"Input file: {args.input}")
-
や --
で始まる名前の引数は、デフォルトで省略可能なオプショナル扱いとなりますが、required=True
フラグをつけると、ユーザーによる指定が必須になります。
上記の例であれば、ユーザーは -i (--input)
オプションを必ず指定する必要があり、args.input
には必ず値が入っていることが保証されます。
この required
よりも、次の default
を使えないかを先に検討してください。
デフォルト値 (default=…)
parser.add_argument(
"-c", "--config", default="app.toml", help="config file (default: %(default)s)"
)
# ...
print(f"Config file: {args.config}")
default=...
パラメーターを指定すると、ユーザーがそのオプションを指定しなかったときのデフォルト値を設定できます。
上記の例であれば、ユーザーが -c (--config)
オプションを省略した場合は app.toml
を指定したものとみなされ、args.config
には必ず値が入っていることが保証されます。
デフォルト値は、help
パラメーターの中で、%(default)s
で参照できます。
ユーザーの引数指定を必須にする required=True
よりも、まずはこの default
パラメーターを使うことを検討すべきです。
on/off フラグ (action=“store_true”)
parser.add_argument(
"-v", "--verbose", action="store_true", help="increase output verbosity"
)
# ...
print(args.verbose)
$ python main.py
False
$ python main.py --verbose
True
action="store_true"
の指定により、True
あるいは False
の 2 値を表現するオプションを定義できます。
ユーザーが -v (--verbose)
オプションを指定したときに、args.verbose
の値が True
になります。
action="store_true"
の代わりに action="store_false"
とすると、True
と False
の扱いが反転します(-v
オプションを指定したときに args.verbose
が False
になります)。
選択肢 (choices=[…])
parser.add_argument(
"-t", "--type", choices=["json", "yaml", "toml"], default="json",
help="file format (default %(default)s)"
)
# ...
print(args.type)
$ python main.py
json
$ python main.py -t yaml
yaml
$ python main.py -t hoge
usage: main.py [-h] [-t {json,yaml,toml}]
main.py: error: argument -t/--type: invalid choice: 'hoge' (choose from 'json', 'yaml', 'toml')
choices=[...]
で選択肢を列挙することによって、ユーザーは制約された選択肢の中からのみ値を指定することができるようになります。
ユーザーが何も入力しなかったときの値は None
となりますが、default="json"
のようにデフォルト値を設定しておくこともできます。
上記の例では文字列の選択肢を定義していますが、数値の選択肢も同様に定義できます。
parser.add_argument("--level", type=int, choices=[1, 2, 3, 4, 5])
parser.add_argument("--level", type=int, choices=range(1, 6)) # 同上
同一オプションが何回指定されたかを取得する (action=“count”)
parser.add_argument("-v", action="count", default=0, help="increase output verbosity")
# ...
print(args.v)
$ python main.py
0
$ python main.py -v
1
$ python main.py -vvv
3
action="count"
とすると、そのオプション自体がユーザーによって何度指定されたかをカウントしてくれます。
冗長レベルを制御するための -v
、-vv
、-vvv
といったオプションはたまに見かけますね。
排他的にしか指定できないオプション (add_mutually_exclusive_group)
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
% python main.py -v -q
usage: main.py [-h] [-v | -q]
main.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
同時に指定すると意味的に矛盾が生じるようなオプション項目は、排他グループを作成してそこに登録しておきます。
上記の例では、-v (--verbose)
オプションと -q (--quiet)
オプションは同じ排他グループに属しているため、どちらかしか指定できません。
ヘルプ表示のラベル名を変更する (metavar)
parser.add_argument("input", metavar="INPUT_FILE", help="input file path")
parser.add_argument("output", metavar="OUTPUT_FILE", help="output file path")
$ python main.py -h
usage: main.py [-h] INPUT_FILE OUTPUT_FILE
...
matavar
パラメーターで、ヘルプに表示されるプレースホルダー名を変更できます。
参照時のプロパティ名を変更する (dest)
parser.add_argument(
"-v", dest="verbose", action="count", default=0, help="increase output verbosity"
)
# ...
print(args.verbose)
通常、定義したオプション名はそのまま Namespace
オブジェクトのプロパティ名となりますが、dest
パラメーターでプロパティ名を変更できます。
上記の例では、-v
オプションを定義しているので、通常は args.v
で参照するところを、args.verbose
で参照できるようにしています。
アプリケーションの説明文
ArgumentParser
コンストラクタのパラメーターで、ヘルプメッセージで表示する詳細説明を追加できます。
長いメッセージは、ターミナルの横幅に応じて適切な位置で改行して表示してくれるので、Python コード上ではどこで改行するかを気にせずに記述できます(ただし、単語の区切りがない日本語はうまくいきません)。
parser = argparse.ArgumentParser(
description="""
Long long long long long long long
long long long long long long long
long long long long long description
"""
)
整形済みのテキストとして、Python コード上での改行位置を保って出力するには、formatter_class
として RawDescriptionHelpFormatter
を指定します。
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent(
"""
Please do not mess up this text!
--------------------------------
I have indented it
exactly the way
I want it
"""
),
)
(おまけ)コマンドライン引数を扱うモジュールを切り出す
多くの機能を含む Python スクリプトは、コマンドライン引数の定義も複雑になりがちです。
argparse
まわりの処理をモジュールと切り出しておくと、メインスクリプトのコードをシンプルに保つことができます。
上記のように切り出したモジュールは、メインスクリプトから次のように使用できます。
すっきり ٩(๑❛ᴗ❛๑)۶