Argparse チュートリアル¶
このチュートリアルでは、argparse を丁寧に説明します。 argparse は、Python 標準ライブラリの一部であり、おすすめのコマンドライン引数の解析モジュールです。
注釈
標準ライブラリには、コマンドライン引数の処理に直接関係するライブラリが、ほかに 2 つあります。
より低水準の optparse モジュールは、アプリケーションに合わせた設定に多くのコードが必要な場合もありますが、argparse がサポートしない動作を指定できます。
さらに低水準の getopt は、C プログラマが使う getopt() 系の関数に相当するものです。
このガイドでは、これらのモジュールを直接は取り上げません。
ただし、argparse の主要な概念の多くは optparse に由来するため、このチュートリアルの一部は optparse の利用者にも役立ちます。
コンセプト¶
ls コマンドを使って、このチュートリアルで私たちが学ぶ機能をいくつか見てみましょう:
$ ls
cpython devguide prog.py pypy rm-unused-function.patch
$ ls pypy
ctypes_configure demo dotviewer include lib_pypy lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x 4 wena wena 4096 Feb 8 12:04 devguide
-rwxr-xr-x 1 wena wena 535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb 7 00:59 pypy
-rw-r--r-- 1 wena wena 741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...
上の4つの実行結果から、以下のことが分かります:
ls コマンドは、まったくオプションを指定せずに実行したとしても役に立ちます。デフォルトの動作は、カレントディレクトリの内容を表示することです。
デフォルト以外の動作をさせるには、追加の情報を渡します。 この例では、別のディレクトリ
pypyの内容を表示させるために、位置引数と呼ばれるものを指定しました。 コマンドライン上の位置だけで、プログラムがその値の用途を判断できることから、この名前が付いています。 この考え方は cp のようなコマンドではさらに重要です。 最も基本的な使い方はcp SRC DESTで、最初の位置は コピーしたいもの、2 番目の位置は コピー先 を表します。プログラムの振る舞いを変えましょう。例では、単にファイル名を表示する代わりにそれぞれのファイルに関する多くの情報を表示します。このケースでは、
-lはoptional引数として知られます。ヘルプテキストの抜粋です。この実行の仕方は、まだ使用したことがないプログラムにたいして行うと有用で、ヘルプテキストを読むことで、プログラムがどのように動作するかわかります。
基礎¶
(ほとんど)何もしない、とても簡単な例から始めましょう:
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
下記がこのコードを実行した結果です:
$ python prog.py
$ python prog.py --help
usage: prog.py [-h]
options:
-h, --help show this help message and exit
$ python prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo
こんなことが起こりました:
オプションなしでスクリプトを実行した結果、なにも標準出力に表示されませんでした。それほど便利ではありませんね。
二つ目の実行結果から
argparseモジュールの有用性がわかります。ほとんど何もしていないのに、すてきなヘルプメッセージが手に入りました。--help(短く-hとも書けます) は、こちらで定義せずに使える唯一のオプションです。 それ以外を指定するとエラーになりますが、その場合も追加の実装なしで役立つ使用方法のメッセージが表示されます。
位置引数の入門¶
以下に例を示します:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)
このコードを実行してみましょう:
$ python prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python prog.py --help
usage: prog.py [-h] echo
positional arguments:
echo
options:
-h, --help show this help message and exit
$ python prog.py foo
foo
こんなことが起こりました:
プログラムが受け付けるコマンドラインオプションを指定するメソッドである
add_argument()を追加しました。ここでは、その機能にあわせて引数名をechoとしました。プログラムを実行すると、オプションを指定するように要求されます。
parse_args()メソッドは、指定されたオプションから得たデータを返します。 この例ではechoに指定された値です。この変数は
argparseが自動的に行うある種の魔法です(つまり、値を格納する変数を指定する必要がありません)。変数の名前がメソッドに与えた文字列引数echoと同じことに気付いたでしょう。
ヘルプメッセージは十分なように見えますが、それほど分かりやすくもありません。たとえば、echo は位置引数であることがわかりますが、それが何であるかを知るためには推測するかソースコードを見なけれなりません。もうすこしヘルプメッセージ分かりやすくしてみましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)
修正した結果は以下のようになります:
$ python prog.py -h
usage: prog.py [-h] echo
positional arguments:
echo echo the string you use here
options:
-h, --help show this help message and exit
次は、もっと有益なことをしてみませんか?:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)
下記がこのコードを実行した結果です:
$ python prog.py 4
Traceback (most recent call last):
File "prog.py", line 5, in <module>
print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
上手くいきませんでした。 何も伝えなければ、argparse は与えられたオプションを文字列として扱います。 argparse にオプションの値を整数として扱うように伝えましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
type=int)
args = parser.parse_args()
print(args.square**2)
下記がこのコードを実行した結果です:
$ python prog.py 4
16
$ python prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'
今度は上手くいきました。このプログラムは不正な入力が与えられるとそれを処理せずに、より親切なメッセージを表示して実行を終了します。
Optional引数の導入¶
ここまで位置引数を扱ってきました。optional引数を追加する方法についても見ていきましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
実行してみましょう:
$ python prog.py --verbosity 1
verbosity turned on
$ python prog.py
$ python prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]
options:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
$ python prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument
こんなことが起こりました:
プログラムは、
--verbosityが指定された場合はなにかしらを表示し、指定されなければ何も表示をしないように書かれています。オプションの指定が実際に任意であることを示すために、プログラムをオプション指定なしで実行してもエラーにはなっていません。オプション引数が指定されなかった場合、関連する変数、この例では
args.verbosity、の値にはデフォルトでNoneがセットされます。これがif文による真偽テストに失敗した理由です。ヘルプメッセージが少し変わりました。
--verbosityオプションを使うには、そのオプションにひとつの値を指定しなければなりません。
上記の例では、--verbosity に任意の整数を取れます。この簡単なプログラムでは、実際には True または False の二つの値だけが有効です。そうなるようにコードを修正してみましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
実行してみましょう:
$ python prog.py --verbose
verbosity turned on
$ python prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python prog.py --help
usage: prog.py [-h] [--verbose]
options:
-h, --help show this help message and exit
--verbose increase output verbosity
こんなことが起こりました:
オプションは値を必要とするのではなく、単なるフラグになりました。この考え方に沿うようにオプションの名前も変更しています。ここで新しいキーワード
actionを指定し、値として"store_true"を設定していることに注意してください。これは、オプションが指定された場合に値としてTrueをargs.verboseに設定するということを意味します。オプションの指定がない場合の値はFalseとなることを意味します。フラグは値を取るべきではないので、値を指定するとプログラムはエラーになります。
ヘルプテキストが変わっています。
短いオプション¶
コマンドラインになれていれば、オプションの短いバージョンの話題に触れていないことに気付いたでしょう。それはとても簡単です:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
上記のプログラムを実行するとこうなります:
$ python prog.py -v
verbosity turned on
$ python prog.py --help
usage: prog.py [-h] [-v]
options:
-h, --help show this help message and exit
-v, --verbose increase output verbosity
新しい機能がヘルプテキストにも反映されている点に気付いたでしょう。
位置引数とOptional引数の併用¶
プログラムが複雑になってきました:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
print(f"the square of {args.square} equals {answer}")
else:
print(answer)
出力は以下のようになります:
$ python prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python prog.py 4
16
$ python prog.py 4 --verbose
the square of 4 equals 16
$ python prog.py --verbose 4
the square of 4 equals 16
位置引数を元に戻したので、引数を指定しないとエラーになりました。
指定する順序は関係ありません。
複数の詳細レベルを値にとれるようにプログラムを元に戻して、指定された値を使ってみましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
実行してみましょう:
$ python prog.py 4
16
$ python prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python prog.py 4 -v 1
4^2 == 16
$ python prog.py 4 -v 2
the square of 4 equals 16
$ python prog.py 4 -v 3
16
プログラムのバグである最後の結果を除いて、上手く行っているようです。このバグを、--verbosity オプションが取れる値を制限することで修正しましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
実行してみましょう:
$ python prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square
positional arguments:
square display a square of a given number
options:
-h, --help show this help message and exit
-v, --verbosity {0,1,2}
increase output verbosity
変更がエラーメッセージとヘルプメッセージの両方に反映されていることに注意してください。
詳細レベルついて、違ったアプローチを試してみましょう。これは CPython 実行可能ファイルがその詳細レベル引数を扱う方法と同じです。( python --help の出力を確認してください):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
もう一つの action である "count" を紹介します。これは指定されたオプションの出現回数を数えます:
$ python prog.py 4
16
$ python prog.py 4 -v
4^2 == 16
$ python prog.py 4 -vv
the square of 4 equals 16
$ python prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python prog.py 4 -h
usage: prog.py [-h] [-v] square
positional arguments:
square display a square of a given number
options:
-h, --help show this help message and exit
-v, --verbosity increase output verbosity
$ python prog.py 4 -vvv
16
前のバージョンのスクリプトのようにフラグ (
action="store_true"に似ています)になりました。エラーとなる理由がわかります。(訳注: 5つ目の例では-vオプションに引数を与えたため、エラーとなっています。)これは"store_true" アクションによく似た動作をします。
では "count" アクションが何をもたらすかデモンストレーションをします。おそらくこのような使用方法を前に見たことがあるでしょう。
-vフラグを指定しなければ、フラグの値はNoneとみなされます。期待通り、長い形式のフラグを指定しても同じ結果になります。
残念ながら、このヘルプ出力はプログラムが新しく得た機能についてそこまで有益ではありません。しかし、スクリプトのドキュメンテーションを改善することでいつでも修正することができます(例えば、
helpキーワード引数を使用することで)。最後の出力はプログラムにバグがあることを示します。
修正しましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
# バグ修正: == を >= に置き換える
if args.verbosity >= 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
これが結果です:
$ python prog.py 4 -vvv
the square of 4 equals 16
$ python prog.py 4 -vvvv
the square of 4 equals 16
$ python prog.py 4
Traceback (most recent call last):
File "prog.py", line 11, in <module>
if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
最初の出力はうまくいき、先ほどのバグが直りました。 2 以上ならどの値でも最も詳しい出力にしたかったので、この動作で正解です。
三番目の結果は、よくありません。
このバグを修正しましょう:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
もう一つのキーワード引数である default を導入しました。整数値と比較できるように、その値に 0 を設定しました。デフォルトでは、optional 引数が指定されていない場合 None となること、None が整数値と比較できない(よって TypeError 例外となる)ことを思い出してください。
こうなりました:
$ python prog.py 4
16
ここまで学んできたことだけで、さまざまな事が実現できます。しかしまだ表面をなぞっただけです。 argparse モジュールはとても強力ですので、チュートリアルを終える前にもう少しだけ探検してみましょう.
もうちょっとだけ学ぶ¶
もし、この小さなプログラムを二乗以外の累乗が行えるように拡張するとどうなるでしょうか:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.x}^{args.y} == {answer}")
else:
print(answer)
出力:
$ python prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python prog.py -h
usage: prog.py [-h] [-v] x y
positional arguments:
x the base
y the exponent
options:
-h, --help show this help message and exit
-v, --verbosity
$ python prog.py 4 2 -v
4^2 == 16
これまで、出力されるテキストを 変更する ために詳細レベルを使ってきました。かわりに下記の例では、追加の テキストを出力するのに詳細レベルを使用します:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print(f"Running '{__file__}'")
if args.verbosity >= 1:
print(f"{args.x}^{args.y} == ", end="")
print(answer)
出力:
$ python prog.py 4 2
16
$ python prog.py 4 2 -v
4^2 == 16
$ python prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16
多義性のある引数の指定¶
ある引数の解釈について、位置引数なのか、ある引数に対する追加の引数なのか、あいまいさがあるときは、 -- を使うことで parse_args() に対してこれ以降は位置引数であることを伝えることができます:
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-n', nargs='+')
>>> parser.add_argument('args', nargs='*')
>>> # 曖昧なので、parse_args はオプションと解釈する
>>> parser.parse_args(['-f'])
usage: PROG [-h] [-n N [N ...]] [args ...]
PROG: error: unrecognized arguments: -f
>>> parser.parse_args(['--', '-f'])
Namespace(args=['-f'], n=None)
>>> # 曖昧なので、-n オプションは引数を取れるだけ取る
>>> parser.parse_args(['-n', '1', '2', '3'])
Namespace(args=[], n=['1', '2', '3'])
>>> parser.parse_args(['-n', '1', '--', '2', '3'])
Namespace(args=['2', '3'], n=['1'])
競合するオプション¶
これまで argparse.ArgumentParser インスタンスの 2 つのメソッドを使ってきました。
3 つ目の add_mutually_exclusive_group() を紹介しましょう。
このメソッドでは、互いに同時指定できないオプションを定義できます。
新しい機能の意味が伝わるよう、プログラムのほかの部分も変更し、--verbose と反対の働きをする --quiet オプションを導入します。
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{args.x}^{args.y} == {answer}")
プログラムは簡単になり、説明のためにいくつかの機能を省きました。 出力を見てみましょう。
$ python prog.py 4 2
4^2 == 16
$ python prog.py 4 2 -q
16
$ python prog.py 4 2 -v
4 to the power 2 equals 16
$ python prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
これは分かりやすいでしょう。ここで得たちょっとした柔軟性を示すために最後の出力を追加しました、つまり長い形式のオプションと短い形式のオプションの混在です。
結びの前に、恐らくあなたはプログラムの主な目的をユーザに伝えたいでしょう。万が一、彼らがそれを知らないときに備えて:
import argparse
parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{args.x}^{args.y} == {answer}")
使用方法のテキストが少し変化しました。[-v | -q] は -v または -q のどちらかを使用できるが、同時に両方を使用できないことを意味します:
$ python prog.py --help
usage: prog.py [-h] [-v | -q] x y
calculate X to the power of Y
positional arguments:
x the base
y the exponent
options:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet
argparse の出力を翻訳するには¶
ヘルプテキストやエラーメッセージなどの argparse モジュールの出力は、すべて gettext モジュールを使って訳せるように作られています。これにより、 argparse によって生成されたメッセージを簡単にローカライズできます。 プログラムやモジュールを国際化する も参照してください。
たとえば、この argparse の出力では:
$ python prog.py --help
usage: prog.py [-h] [-v | -q] x y
calculate X to the power of Y
positional arguments:
x the base
y the exponent
options:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet
文字列 usage:, positional arguments:, options: と show this help message and exit はすべて訳せます。
これらの文字列を訳すには、それらをまず .po ファイルに抽出する必要があります。たとえば、 Babel を使い、次のコマンドを実行します。
$ pybabel extract -o messages.po /usr/lib/python3.12/argparse.py
このコマンドは argparse モジュールからすべての翻訳可能な文字列を抽出し、それらを messages.po という名前のファイルに出力します。このコマンドは Python のインストールが /usr/lib にあることを前提としています。
システム上の argparse モジュールの場所は、次のスクリプトで調べられます:
import argparse
print(argparse.__file__)
.po ファイル内のメッセージが訳され、その訳が gettext を用いてインストールされると、 argparse は翻訳されたメッセージを表示できるようになります。
独自の型変換¶
argparse モジュールでは、コマンドライン引数に独自の型変換処理を指定できます。
これにより、利用者の入力を argparse.Namespace に格納する前に変換できます。
プログラムで入力を使う前に前処理が必要な場合に役立ちます。
独自の型変換処理には、引数の値を表す文字列を 1 つ受け取り、変換した値を返す任意の呼び出し可能オブジェクトを使えます。 もっと複雑な処理が必要な場合は、代わりに action 引数で独自のアクションクラスを指定できます。
例えば、異なるプレフィクスの引数を受け取り、それぞれに応じて処理したいとします。
import argparse
parser = argparse.ArgumentParser(prefix_chars='-+')
parser.add_argument('-a', metavar='<value>', action='append',
type=lambda x: ('-', x))
parser.add_argument('+a', metavar='<value>', action='append',
type=lambda x: ('+', x))
args = parser.parse_args()
print(args)
出力:
$ python prog.py -a value1 +a value2
Namespace(a=[('-', 'value1'), ('+', 'value2')])
この例では、次のことを行っています。
prefix_chars引数を使い、独自のプレフィクス文字を持つパーサを作りました。-aと+aという 2 つの引数を定義しました。 それぞれのtype引数で独自の型変換処理を指定し、値をプレフィクスと一緒にタプルに格納するようにしました。
独自の型変換処理がなければ、-a と +a は同じ引数として扱われ、望ましくない結果になります。
型変換処理を使うことで、2 つの引数を区別できました。
結び¶
argparse モジュールはここで学んだことより多くの機能を提供します。モジュールのドキュメントはとても詳細で、綿密で、そしてたくさんの例があります。このチュートリアルの体験したことで、気がめいることなくそれらの他の機能を会得できるに違いありません。