閉じる

Pythonのsys.argvから卒業 argparseでオプション解析を快適にする

Pythonでコマンドライン引数を扱うと、最初はsys.argvで十分に見えます。

しかし引数が増えると検証や--helpの整備、エラーハンドリングが負担になります。

標準ライブラリのargparseは、ヘルプ生成から型チェックまで自動化して、堅牢なCLIを簡単に作れるので、初心者の方でも段階的に習得できます。

Pythonのsys.argvの限界とargparseのメリット

コマンドライン引数で起きがちなミス

sys.argvは「生の文字列リスト」を返すだけです。

数が足りない、型が違う、未知のオプションが来る、といった状況で自分で全部チェックしなければなりません。

次の例はシンプルですが、すぐに壊れやすくなります。

Python
# 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のようにaddcommitといった動詞を分けられる。

まずは最小の例を見てみます。

Python
# 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()
Shell
$ 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は引数定義とヘルプ生成の中心です。

descriptionepilogを設定して、分かりやすいヘルプを作れます。

Python
# 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に引数名だけを書くと位置引数になります。

Python
# 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'を使います。

Python
# 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で終了します。

Python
# 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を使います。

Python
# 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")のように同時に与えます。

短縮は入力が楽、ロングは自己説明的です。

両対応にしておくとユーザー体験が良いです。

Python
# 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個以上、数値は固定個数です。

Python
# 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個(省略時はNoneconst)
*0個以上の任意個数
+1個以上の任意個数

メモ: 可変長を使うときはmetavarで見出し名を工夫するとヘルプが分かりやすくなります。

相互排他オプション(mutually exclusive)

同時に指定できないオプションはadd_mutually_exclusive_groupで排他にします。

排他を宣言しておくと使い方を間違えにくくなります

Python
# 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も具体的に書くと親切です。

Python
# 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を使います。

サブコマンドごとに引数や処理を分離でき、規模が大きくても見通しを保てます

Python
# 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を捕捉して終了コードを見る例を示します(実運用では捕捉しないのが普通です)。

Python
# 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時に例外を送出させ、独自メッセージを出したり、ログに記録してから終了するといった制御が可能です。

Python
# 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を用意
複雑な処理サブコマンド化(deployrollback)して学習コストを下げる
可変長入力nargsmetavarでヘルプを分かりやすく

ヘルプは最初のUIです。

説明文helpに単語だけでなく、値の例や単位、デフォルトの意図を書くと親切です。

まとめ

argparseは、sys.argvでは手作業になりがちなヘルプ生成、型・個数の検証、エラーメッセージ、サブコマンド分割までを標準機能で提供します

本記事のサンプルのように、add_argumentの基本からnargsや相互排他、subparsersまで押さえると、小規模から中規模のCLIを安全に設計できます。

最初はシンプルな設計で始め、必要に応じてchoicesrequiredmetavarexit_on_errorなどの機能を段階的に取り入れていくと良いです。

「分かりやすいヘルプ」と「堅牢な検証」を両立させて、快適なコマンドライン体験を育てていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!