Python

【Python】argparseのよく使う奴だけまとめたサンプルコード集

Pythonでコマンドライン引数を処理する時に使用するargparseについて、よく使うコードのサンプルをまとめました。
(動作確認時のPythonバージョン:Python 3.10.10)

基本編

位置引数とオプション引数について

argparseで扱うコマンドライン引数は大きく分けて2種類あります。

  • 位置引数
    コマンドライン上での位置(空白区切り)で扱う引数
    通常は省略不可の引数を作成する場合に使用する
  • オプション引数
    ‘-‘始まりの文字列(-f, –fooなど)を指定して扱う引数
    通常は必須ではない引数を作成する場合に使用する
位置引数はコマンドライン上での位置・順番で値を扱う オプション引数は、通常は指定した語句の直後の値を扱う

位置引数とオプション引数を知っていれば、後は構文通りに書くだけなので次はサンプルコードを見ていきましょう。
(細かい説明はコード中にコメントで記載します)

argparse使用例(サンプルコード)

import argparse


def main():
    # ①引数解析用のパーサーを作成
    parser = argparse.ArgumentParser()

    # ②コマンドライン引数の定義をadd_argument()で追加
    #   位置引数 ('-'無しの名前を指定する。定義した順で一つ目・二つ目...と扱われる)
    parser.add_argument("arg1")
    parser.add_argument("arg2")
    #   オプション引数 ('-'有りの名前を指定する。順不同)
    parser.add_argument("--opt1")
    parser.add_argument("--opt2")

    # ③コマンドライン引数解析を実行 (デフォルトで処理されるのでargvとかの指定は不要)
    args = parser.parse_args()

    # ④引数解析の結果確認 (parse_args()の返り値)
    #   ②で定義した名前でドットアクセスが可能 (オプション引数は'-'を除いた名前)
    print("> arg1={}, arg2={}, opt1={}, opt2={}".format(args.arg1, args.arg2, args.opt1, args.opt2))


if __name__ == "__main__":
    main()

上記のサンプルコードを実行すると以下の用になります。

argparse使用例(実行結果)

# 全て指定
python argparse_sample.py aaa bbb --opt1 123 --opt2 456
> arg1=aaa, arg2=bbb, opt1=123, opt2=456

# オプション引数省略 (省略時のデフォルトはNone)
python argparse_sample.py aaa bbb
> arg1=aaa, arg2=bbb, opt1=None, opt2=None

# 位置引数は必須引数なので省略するとエラーとなる
python argparse_sample.py aaa
> usage: argparse_sample.py [-h] [--opt1 OPT1] [--opt2 OPT2] arg1 arg2
> argparse_sample.py: error: the following arguments are required: arg2

# オプション引数と位置引数は逆でも可
# (位置引数はオプション引数を除いた順で一つ目、二つ目と扱われる)
python argparse_sample.py --opt1 123 --opt2 456 aaa bbb
> arg1=aaa, arg2=bbb, opt1=123, opt2=456

応用編

デフォルト値

オプション引数は未指定時にデフォルト値としてNoneが設定されます。
デフォルト値はadd_argument()での引数追加時に個別に設定可能です。

サンプルコード

import argparse


def main():
    parser = argparse.ArgumentParser()

    # デフォルト値が123のオプション引数を定義
    parser.add_argument("--opt1", default=123)

    args = parser.parse_args()
    print("> {}".format(args.opt1))


if __name__ == "__main__":
    main()

実行結果

# 未指定時はdefaultに設定した値が格納される
python default_sample.py
> 123

# 指定時は指定した値が格納される
python default_sample.py --opt 456
> 456

オプション引数を必須引数として扱う

「「「オプション引数だけど必須です!!!」」」
とかいう矛盾を実現したい場合に使います。
なんだそのクソ仕様と思うかもしれませんが、割と見かけます。

サンプルコード

import argparse


def main():
    parser = argparse.ArgumentParser()

    # '-'で始まる引数は通常必須ではないが、
    # required=Trueと設定することで必須オプションになる
    parser.add_argument("--opt1", required=True)

    args = parser.parse_args()
    print("> {}".format(args.opt1))


if __name__ == "__main__":
    main()

引数を辞書に変換

引数解析を行うparse_args()関数はargparse.Namespace型の値を返します。
Namespace型ではなく辞書型として扱いたい場合は、以下で変換が可能です。

サンプルコード

import argparse


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("arg1")
    parser.add_argument("--opt1")
    args = parser.parse_args()

    # vars()を用いることで辞書への一括変換が可能
    args_dict = vars(args)

    for k, v in args_dict.items():
        print("{}: {}".format(k, v))


if __name__ == "__main__":
    main()

型チェック・異常値チェック

argparseでは引数解析時に型チェック・異常値チェックが可能です。

サンプルコード

import argparse


def main():
    parser = argparse.ArgumentParser()

    # 整数値以外が指定された場合はエラー
    parser.add_argument("--opt1", type=int)

    # a, b, c以外が指定された場合はエラー
    parser.add_argument("--opt2", choices=("a", "b", "c"))

    # 1~10の整数値以外が指定された場合はエラー
    parser.add_argument("--opt3", type=int, choices=range(1, 10))

    # 独自のエラーチェックを行う場合はtypeに独自の関数を指定
    parser.add_argument("--opt4", type=validate_hoge)

    args = parser.parse_args()
    print("> opt1={}, opt2={}, opt3={}, opt4={}".format(args.opt1, args.opt2, args.opt3, args.opt4))


def validate_hoge(arg_str):
    """
    独自のエラーチェック関数

    引数に"x64"が含まれない場合にエラーにする例
    """
    if "x64" not in arg_str:
        # エラーにする場合はargparse.ArgumentTypeError()の例外を
        # 使うとエラーメッセージもいい感じに扱ってくれるので便利
        raise argparse.ArgumentTypeError("you must include x64")

    # ここで返す値がparse_args()の返り値に相当
    # (=値の変換を行うことも可能)
    return arg_str

if __name__ == "__main__":
    main()

ロングオプション/ショートオプション

オプション引数は複数の名前を設定することが可能です。
–foo/-fのようなロングオプション/ショートオプションの作成が可能です。

サンプルコード

import argparse


def main():
    parser = argparse.ArgumentParser()

    # オプション引数は複数の名前を設定可能
    parser.add_argument("--option1", "--opt1", "-o")

    # 複数の名前を設定した場合でも、
    # parse_args()の返り値に設定されるのは先頭の名前のみなので注意
    args = parser.parse_args()
    print("> {}".format(args.option1))


if __name__ == "__main__":
    main()

フラグとしてのオプション引数

オプション引数が指定された場合はTrue、未指定の場合はFalse、といった、フラグのような動作をさせたい場合は以下のように記述します。

サンプルコード

import argparse


def main():
    parser = argparse.ArgumentParser()

    # action="store_true":指定時にTrueを設定する
    # (--opt1指定時はTrue, --opt1未指定時はFalse)
    parser.add_argument("--opt1", action="store_true")

    # action="store_false":指定時にFalseを設定する
    # (--opt2指定時はFalse, --opt2未指定時はTrue)
    parser.add_argument("--opt2", action="store_false")

    args = parser.parse_args()
    print("> {} {}".format(args.opt1, args.opt2))


if __name__ == "__main__":
    main()

エラー・例外処理

argparseでは引数解析(parse_args())に失敗するとプログラムが異常終了します。

通常、引数解析はプログラムの最初に行われるのでこの動作は自然なのですが、諸々の理由により異常終了すると困る場合は、以下で回避が可能です。

サンプルコード
テストコード等で例外捕捉したい場合は以下で十分かと思います。

    try:
        args = parser.parse_args()

    except SystemExit as e:
        print(e.code)

ただ、上記の場合、コンソールへのエラーメッセージが垂れ流しとなります。
見た目的にそれを防ぎたい場合は以下のように記載します。

import argparse
from io import StringIO
from contextlib import redirect_stderr


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("arg1", type=int)

    io = StringIO()
    try:
        # with句の間だけ標準エラー出力をリダイレクトする
        with redirect_stderr(io):
            args = parser.parse_args()
    except SystemExit as e:
        print(e.code)
        # リダイレクトした標準エラー出力は、getvalue()で参照可能
        print(io.getvalue())


if __name__ == "__main__":
    main()


Python 3.9以降は、exit_on_errorというフラグが使用でき、exit_on_error=Falseで如何にも異常終了しなくなりそうなのですが、このフラグはエラーによって動作が異なるので注意です。

  • 型エラー等:argparse.ArgumentErrorの例外発生
  • 必須引数の不足等:異常終了する(SystemExitの例外発生)
import argparse


def main():
    parser = argparse.ArgumentParser(exit_on_error=False)
    parser.add_argument("arg1", type=int)

    try:
        args = parser.parse_args()
        print("> {}".format(args))
    except argparse.ArgumentError:
        # arg1がint以外の場合はこのルートに入る
        # arg1が未指定の場合は例外捕捉されず、異常終了する
        print("Catching an argumentError")


if __name__ == "__main__":
    main()

参考

公式ドキュメント

argparse --- コマンドラインオプション、引数、サブコマンドのパーサー
ソースコード: Lib/argparse.py チュートリアル: このページは API のリファレンス情報が記載しています。 argparse チュートリアル では、コマンドラインの解析についてより優しく説明しています。 argparse ...

サブコマンドの使い方

かなりニッチですが、サブコマンドの使い方も以前まとめたので興味のある方は是非。

コメント

タイトルとURLをコピーしました