Pythonでコマンドライン引数を扱うと、最初はsys.argv
で十分に見えます。
しかし引数が増えると検証や--help
の整備、エラーハンドリングが負担になります。
標準ライブラリのargparse
は、ヘルプ生成から型チェックまで自動化して、堅牢なCLIを簡単に作れるので、初心者の方でも段階的に習得できます。
Pythonのsys.argvの限界とargparseのメリット
コマンドライン引数で起きがちなミス
sys.argv
は「生の文字列リスト」を返すだけです。
数が足りない、型が違う、未知のオプションが来る、といった状況で自分で全部チェックしなければなりません。
次の例はシンプルですが、すぐに壊れやすくなります。
# bad_sysargv.py
# 目的: "名前 年齢" を受け取って挨拶するだけのスクリプト
import sys
def main():
# sys.argv [0] はスクリプト名、以降が引数
name = sys.argv[1] # 引数が足りないと IndexError
age = int(sys.argv[2]) # 数字でないと ValueError
print(f"Hello {name} ({age})")
if __name__ == "__main__":
main()
$ python bad_sysargv.py Alice
Traceback (most recent call last):
File "bad_sysargv.py", line 9, in <module>
main()
File "bad_sysargv.py", line 7, in main
name = sys.argv[1]
IndexError: list index out of range
$ python bad_sysargv.py Alice abc
Traceback (most recent call last):
File "bad_sysargv.py", line 9, in <module>
main()
File "bad_sysargv.py", line 8, in main
age = int(sys.argv[2])
ValueError: invalid literal for int() with base 10: 'abc'
このように、使い勝手のよいヘルプや丁寧なエラーメッセージ、デフォルト値や型検証を自前で書くのは大変です。
argparseが自動で解決すること(–helpや検証)
argparse
を使うと、次のような点が自動化されます。
- ヘルプ生成:
-h
/--help
で使い方を自動表示。 - 型変換と検証:
type=int
などで自動チェック。エラー時は整ったメッセージと適切な終了コード。 - デフォルト値:
default
で指定。 - フラグ:
--verbose
のような真偽フラグを簡単に実装。 - サブコマンド:
git
のようにadd
やcommit
といった動詞を分けられる。
まずは最小の例を見てみます。
# good_argparse_min.py
# 目的: "名前" と任意の "--age" を受け取って挨拶
import argparse
def main():
parser = argparse.ArgumentParser(description="挨拶スクリプト")
parser.add_argument("name", help="相手の名前")
parser.add_argument("--age", type=int, default=20, help="年齢(省略時は20)")
args = parser.parse_args()
print(f"Hello {args.name} ({args.age})")
if __name__ == "__main__":
main()
$ python good_argparse_min.py -h
usage: good_argparse_min.py [-h] [--age AGE] name
挨拶スクリプト
positional arguments:
name 相手の名前
options:
-h, --help show this help message and exit
--age AGE 年齢(省略時は20)
$ python good_argparse_min.py Alice --age 30
Hello Alice (30)
ヘルプと検証が自動化されるだけで、CLIの信頼性と使いやすさが一気に向上します。
Python argparseの基本の使い方
ArgumentParserの作成と–help
ArgumentParser
は引数定義とヘルプ生成の中心です。
description
やepilog
を設定して、分かりやすいヘルプを作れます。
# basics_help.py
import argparse
def main():
parser = argparse.ArgumentParser(
prog="greet",
description="名前を受け取って挨拶します",
epilog="例: greet Taro --times 3"
)
# 今は引数を定義しない。-h/--helpは自動で使えます
parser.parse_args() # 実際にはここで解析を行う
if __name__ == "__main__":
main()
ヘルプ表示:
$ python basics_help.py -h
usage: greet [-h]
名前を受け取って挨拶します
options:
-h, --help show this help message and exit
例: greet Taro --times 3
位置引数を追加する(add_argument)
位置引数はコマンド名の後に順番で与える値です。
add_argument
に引数名だけを書くと位置引数になります。
# positional.py
import argparse
def main():
parser = argparse.ArgumentParser(description="ファイル内容を表示する例")
parser.add_argument("path", help="読み込むファイルパス")
args = parser.parse_args()
# デモ: 実際のファイル読み込みは省略、ここでは引数だけ表示
print(f"path={args.path}")
if __name__ == "__main__":
main()
$ python positional.py data.txt
path=data.txt
オプション引数とフラグ(–verbose)
オプション引数は--name
のような形で、順序に依存せず指定できます。
真偽フラグはaction='store_true'
を使います。
# option_flag.py
import argparse
def main():
parser = argparse.ArgumentParser(description="verboseフラグの例")
parser.add_argument("message", help="表示するメッセージ")
parser.add_argument("--verbose", "-v", action="store_true",
help="詳細ログを表示するフラグ")
args = parser.parse_args()
if args.verbose:
print("[DEBUG] starting...")
print(args.message)
if args.verbose:
print("[DEBUG] done.")
if __name__ == "__main__":
main()
実行例(フラグなし/あり):
$ python option_flag.py "Hello"
Hello
$ python option_flag.py "Hello" --verbose
[DEBUG] starting...
Hello
[DEBUG] done.
型(type)とデフォルト(default)
type
で自動変換、default
で省略時の値を設定できます。
変換に失敗すると丁寧なエラーとともに終了コード2で終了します。
# type_default.py
import argparse
from time import sleep
def main():
parser = argparse.ArgumentParser(description="メッセージを指定回数表示します")
parser.add_argument("message", help="表示するメッセージ")
parser.add_argument("--count", "-c", type=int, default=1,
help="繰り返し回数(デフォルト: 1)")
parser.add_argument("--delay", "-d", type=float, default=0.0,
help="各表示の間隔秒(デフォルト: 0)")
args = parser.parse_args()
for i in range(args.count):
print(f"{i+1}: {args.message}")
if args.delay > 0:
sleep(args.delay)
if __name__ == "__main__":
main()
$ python type_default.py "Hi" -c 3 -d 0.1
1: Hi
2: Hi
3: Hi
型エラー時:
$ python type_default.py "Hi" -c abc
usage: type_default.py [-h] [--count COUNT] [--delay DELAY] message
type_default.py: error: argument --count/-c: invalid int value: 'abc'
必須(required)と選択肢(choices)
必須オプションはrequired=True
、取り得る値を限定するにはchoices
を使います。
# required_choices.py
import argparse
def main():
parser = argparse.ArgumentParser(description="圧縮のモードとレベルを指定する例")
parser.add_argument("--mode", required=True, choices=["fast", "slow"],
help="圧縮モードを選択(fast|slow)")
parser.add_argument("--level", type=int, choices=[1, 2, 3], default=2,
help="圧縮レベル(1/2/3)。省略時は2")
args = parser.parse_args()
print(f"mode={args.mode}, level={args.level}")
if __name__ == "__main__":
main()
必須未指定:
$ python required_choices.py
usage: required_choices.py [-h] --mode {fast,slow} [--level {1,2,3}]
required_choices.py: error: the following arguments are required: --mode
選択肢エラー:
$ python required_choices.py --mode ultra
usage: required_choices.py [-h] --mode {fast,slow} [--level {1,2,3}]
required_choices.py: error: argument --mode: invalid choice: 'ultra' (choose from 'fast', 'slow')
成功例:
$ python required_choices.py --mode fast --level 3
mode=fast, level=3
コマンドライン引数の定番パターン
短縮(-v)とロング(–verbose)の両対応
短い別名と長い名前はadd_argument("-v", "--verbose")
のように同時に与えます。
短縮は入力が楽、ロングは自己説明的です。
両対応にしておくとユーザー体験が良いです。
# short_long.py
import argparse
def main():
parser = argparse.ArgumentParser(description="短縮とロングの両対応例")
parser.add_argument("--verbose", "-v", action="store_true",
help="詳細表示を有効化")
args = parser.parse_args()
print(f"verbose={args.verbose}")
if __name__ == "__main__":
main()
$ python short_long.py -v
verbose=True
$ python short_long.py --verbose
verbose=True
複数値を受け取る(nargs)
nargs
で値の個数を制御できます。
'+'
は1個以上、'*'
は0個以上、数値は固定個数です。
# nargs_numbers.py
import argparse
from statistics import mean
def main():
parser = argparse.ArgumentParser(description="複数数値の統計")
parser.add_argument("numbers", nargs="+", type=float, metavar="NUM",
help="1個以上の数値")
args = parser.parse_args()
total = sum(args.numbers)
avg = mean(args.numbers)
print(f"count={len(args.numbers)} total={total} avg={avg:.2f}")
if __name__ == "__main__":
main()
$ python nargs_numbers.py 10 20 30
count=3 total=60.0 avg=20.00
代表的なnargs
の指定は次の通りです。
指定 | 意味 |
---|---|
省略 | ちょうど1個 |
n(例: 3) | ちょうどn個 |
? | 0個または1個(省略時はNone かconst ) |
* | 0個以上の任意個数 |
+ | 1個以上の任意個数 |
メモ: 可変長を使うときはmetavar
で見出し名を工夫するとヘルプが分かりやすくなります。
相互排他オプション(mutually exclusive)
同時に指定できないオプションはadd_mutually_exclusive_group
で排他にします。
排他を宣言しておくと使い方を間違えにくくなります。
# mutually_exclusive.py
import argparse
def main():
parser = argparse.ArgumentParser(description="バックアップのログ出力例")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--quiet", "-q", action="store_true", help="静かに実行")
group.add_argument("--verbose", "-v", action="store_true", help="詳細に実行")
args = parser.parse_args()
print("mode=quiet" if args.quiet else "mode=verbose")
if __name__ == "__main__":
main()
$ python mutually_exclusive.py -q -v
usage: mutually_exclusive.py [-h] (--quiet | --verbose)
mutually_exclusive.py: error: argument --verbose/-v: not allowed with argument --quiet/-q
ヘルプ文と表示名(metavar)
ヘルプの引数名はmetavar
で見やすくできます。
説明文help
も具体的に書くと親切です。
# metavar_help.py
import argparse
def main():
parser = argparse.ArgumentParser(description="ファイルのアップロード例")
parser.add_argument("files", nargs="+", metavar="FILE",
help="アップロードする1個以上のファイルパス")
parser.add_argument("--dest", metavar="URL", required=True,
help="アップロード先URL")
args = parser.parse_args()
print(f"Upload {len(args.files)} file(s) to {args.dest}")
if __name__ == "__main__":
main()
ヘルプ表示:
$ python metavar_help.py -h
usage: metavar_help.py [-h] --dest URL FILE [FILE ...]
ファイルのアップロード例
positional arguments:
FILE アップロードする1個以上のファイルパス
options:
-h, --help show this help message and exit
--dest URL アップロード先URL
小さなCLIを作る実践
サブコマンド(subparsers)の基本
git
のようにコマンドを分けるにはadd_subparsers
を使います。
サブコマンドごとに引数や処理を分離でき、規模が大きくても見通しを保てます。
# calc_cli.py
import argparse
import operator
def add_cmd(args):
print(args.x + args.y)
def mul_cmd(args):
print(args.x * args.y)
def main():
parser = argparse.ArgumentParser(prog="calc", description="簡易計算CLI")
subparsers = parser.add_subparsers(dest="command", required=True, help="サブコマンド")
# add サブコマンド
p_add = subparsers.add_parser("add", help="加算を行う")
p_add.add_argument("x", type=float, help="左辺")
p_add.add_argument("y", type=float, help="右辺")
p_add.set_defaults(func=add_cmd)
# mul サブコマンド
p_mul = subparsers.add_parser("mul", help="乗算を行う")
p_mul.add_argument("x", type=float, help="左辺")
p_mul.add_argument("y", type=float, help="右辺")
p_mul.set_defaults(func=mul_cmd)
args = parser.parse_args()
# 各サブコマンドで set_defaults した関数にディスパッチ
args.func(args)
if __name__ == "__main__":
main()
ヘルプ表示:
$ python calc_cli.py -h
usage: calc [-h] {add,mul} ...
簡易計算CLI
positional arguments:
{add,mul} サブコマンド
add 加算を行う
mul 乗算を行う
options:
-h, --help show this help message and exit
$ python calc_cli.py add 3 5
8.0
$ python calc_cli.py mul 2 4.5
9.0
エラー時のメッセージと終了コード
argparseはエラー時に終了コード2で終了し、使い方のヒントを表示します。
学習用にSystemExit
を捕捉して終了コードを見る例を示します(実運用では捕捉しないのが普通です)。
# exit_codes.py
import argparse
def main():
parser = argparse.ArgumentParser(prog="demo", description="終了コードのデモ")
parser.add_argument("--level", type=int, choices=[1, 2, 3], required=True,
help="レベル(1/2/3)")
try:
parser.parse_args() # ここでエラーがあれば SystemExit が送出されます
print("OK: 終了コード0で終了予定")
except SystemExit as e:
# ここに来るのは parse_args がエラーを見つけたとき
print(f"argparseが終了コード {e.code} で終了しました")
if __name__ == "__main__":
main()
実行例(必須欠落):
$ python exit_codes.py
usage: demo [-h] --level {1,2,3}
demo: error: the following arguments are required: --level
argparseが終了コード 2 で終了しました
補足: Python 3.9以降はArgumentParser(exit_on_error=False)
でparse_args
時に例外を送出させ、独自メッセージを出したり、ログに記録してから終了するといった制御が可能です。
# exit_on_error_false.py
import argparse
import sys
def main():
parser = argparse.ArgumentParser(prog="demo2", exit_on_error=False)
parser.add_argument("--port", type=int, help="ポート番号")
try:
parser.parse_args(["--port", "not-int"]) # デモのため擬似引数を渡す
except Exception as e:
print(f"独自ハンドリング: {e}", file=sys.stderr)
sys.exit(2) # 好みの終了コードを返す
if __name__ == "__main__":
main()
独自ハンドリング: argument --port: invalid int value: 'not-int'
引数設計のコツ(初心者向け)
引数は「ユーザーが迷わないこと」を最優先に設計します。
ロングオプションは意味が伝わる名詞か動詞にし、デフォルトは安全側に寄せます。
位置引数は最小限にし、ほとんどはオプション化するとヘルプが読みやすくなります。
ケース | 推奨設計 |
---|---|
真偽フラグ | --verbose や--dry-run のようにポジティブ名で、デフォルトFalse |
必須の設定 | オプションでもrequired=True を使い、ヘルプに理由を書く |
危険操作 | --force など明示的な名前にし、確認プロンプトや--dry-run を用意 |
複雑な処理 | サブコマンド化(deploy やrollback )して学習コストを下げる |
可変長入力 | nargs とmetavar でヘルプを分かりやすく |
ヘルプは最初のUIです。
説明文help
に単語だけでなく、値の例や単位、デフォルトの意図を書くと親切です。
まとめ
argparseは、sys.argv
では手作業になりがちなヘルプ生成、型・個数の検証、エラーメッセージ、サブコマンド分割までを標準機能で提供します。
本記事のサンプルのように、add_argument
の基本からnargs
や相互排他、subparsers
まで押さえると、小規模から中規模のCLIを安全に設計できます。
最初はシンプルな設計で始め、必要に応じてchoices
やrequired
、metavar
、exit_on_error
などの機能を段階的に取り入れていくと良いです。
「分かりやすいヘルプ」と「堅牢な検証」を両立させて、快適なコマンドライン体験を育てていきましょう。