argparseで引数を受け取る

はじめに

今週の記事もPythonスクリプトで引数を受け取って使う話です。前回はsys.argvつかって受け取る方法を紹介していましたが、今回は便利な専用モジュールのargparseを紹介します。

これを使うと、引数を変数に自動的に格納したり、オプション引数やフラグを作成したり、ヘルプ機能を自動的に作ってくれたりします。

よくUnix/Linux コマンドでは -o filename みたいな感じで出力先ファイルを指定できたりしますが、これをsys.argvで実装しようとすると、配列を全部見て-oがあるかどうか確にして、その次の値をfilenameとして取得して、みたいな結構面倒な処理を自分で作る必要があります。-oが複数出てきたらどうするかとか、-oの次にファイル名がなかった場合のハンドリングとか色々考えないといけないのでとても面倒です。こういう手間を削減してくれます。

順に使い方書いていきますが、ドキュメントはこちらです。
参考: argparse — コマンドラインオプション、引数、サブコマンドのパーサー — Python 3.12.1 ドキュメント

基本的な使い方

ざっくりいうと、argparseは次の3手順で使います。

  • ArgumentParserオブジェクトの作成
  • 必要な引数をパーサーオブジェクトに追加する
  • 引数を解析して結果を取得する

一回単純なサンプル作ってやってみましょう。上記ドキュメントの例をそのまま使います。
sample.py というファイル名で次のスクリプトを作成し、実行権限を `$ chmod u+x sample.py` でつけておきます。

#!/usr/bin/env python
import argparse


# パーサーオブジェクトの作成
parser = argparse.ArgumentParser(
    prog="ProgramName",
    description="What the program does",
    epilog="Text at the bottom of help"
)

# 必要な引数の追加
parser.add_argument("filename")  # 位置引数
parser.add_argument("-c", "--count")  # 値を取るオプション 
parser.add_argument("-v", "--verbose", action="store_true")  # on/off フラグ

# 引数の解析
args = parser.parse_args()
print(args.filename, args.count, args.verbose)

だいたいイメージできると思うのですが、./sample.py を実行する時、最初の引数が filename に格納されて、 -c か –count で指定した値が count変数に格納され、 -v を選択したかどうかがTrue/False で verbos に入ります。 色々やってみましょう。

$ ./sample.py test.txt
test.txt None False

$ ./sample.py test.txt -c 4 -v
test.txt 4 True

$ ./sample.py -v --count abc test.txt
test.txt abc True

$ ./sample.py test1.txt test2.txt 
usage: ProgramName [-h] [-c COUNT] [-v] filename
ProgramName: error: unrecognized arguments: test2.txt

$ ./sample.py                    
usage: ProgramName [-h] [-c COUNT] [-v] filename
ProgramName: error: the following arguments are required: filename

$ ./sample.py --help
usage: ProgramName [-h] [-c COUNT] [-v] filename

What the program does

positional arguments:
  filename

options:
  -h, --help            show this help message and exit
  -c COUNT, --count COUNT
  -v, --verbose

Text at the bottom of help

はい、最初の3例が正しくコマンドを打ったケースでしたが、だいたいイメージ通りに引数を受け取れていることが確認できると思います。
4つ目は位置引数を過剰に設定、5つ目は逆に指定しませんでしたが、それぞれちゃんとエラー文を出してくれていますね。
6個目の例は–helpをつけていますが、なんと自動的にヘルプメッセージを作成して表示してくれています。

コマンド名が ProgramName になっていますが、これはパーサーを作成したときのprog 引数をプログラム名として使っているからです。progを省略すると、ファイル名が使われます。

これは大事なことなのですが、プログラム名=ファイル名のことが多いと思うので、基本的に省略した方がいいと思います。(さっきの例は公式ドキュメントをただ真似しただけ。)

descriptionでプログラム中身の説明、epilogでヘルプの最後に表示するメッセージを指定できますが、これらもどちらも省略可能です。ただ、descriptionは何か書いていておいた方がいいと思います。

ここから細かく仕様を見ていきます。

引数の種類

引数の種類としては、コマンドの後に何番目に渡されたかどうかで扱いが決まる位置引数と、-(ハイフン)付きの名前で始まるオプション引数があります。

argparseは接頭辞の”-“を特別な文字として扱って、これによって挙動を変えています。

上の例でもわかりますが、次のように複数の名前を指定することもできますし、1種類だけの名前でも良いです。このとき注意しないといけないのは、参照するときの変数名です。

-c みたいな短い名前だけの時は – をとって c として参照しますが、-c, –count という2種類の名前を指定した場合は、一番最初に登場する長い名前、が採用されます。長い名前というのは文字列の長さの話ではなく、 – ではなく、 — で始まる引数ということです。
つまり、次のように3つの名前をつけたら、長い名前の中で最初に登場した countが採用されるということです。

#!/usr/bin/env python
import argparse


# パーサーオブジェクトの作成
parser = argparse.ArgumentParser()

# 必要な引数の追加
parser.add_argument("-b")
parser.add_argument("-c", "--count", "--cnt")

# 引数の解析
args = parser.parse_args()
# 短い名前 -b しかないのでbでアクセス
print(args.b)

# --count と --cnt が長い名前だが、先に登場したcountの方が優先
print(args.count)

add_argument の引数

add_argument には様々な引数を指定でき、各種の設定を行うことができます。
全部紹介するのも大変なので一部抜粋して紹介しますが、公式ドキュメントの該当欄の一読をお勧めします。
参考: add_argument() メソッド

default ・・・ コマンドラインに対応する引数が存在せず、さらに namespace オブジェクトにも存在しない場合に利用されるデフォルト値。
type ・・・ データ型。 int や float、ユーザー定義の型など色々指定できる。省略すると文字列(str)。
choices – 引数として許される値のシーケンス。
help ・・・ 引数の説明。-h や –help を使用した時に使われる。
nargs ・・・ 受け取れるコマンドライン引数の数。後で説明します。
action ・・・ コマンドラインにこの引数があったときの動作。後で説明します。

だいたいはイメージ通りの挙動をしてくれるのですが、nargsとactionについてはこの後説明します。

受け取れるコマンドライン引数の数について

nargs という値を使って、受け取れる引数の数を指定できます。

nargs の指定は正規表現風になっています。 整数Nを指定すればその個数、?なら1個か0個で、0だったらdefalut値が使われます。*とすると任意の数受け取れます。また、+だとこちらも任意の数受け取れますが、0個だった場合にエラーが起きます。

例えば、任意の数のファイルのデータを入力として、1個のファイルに結果を書き出すようなコマンドがあったとしましょう。(というより、tar コマンドでアーカイブ作る時はそういう指定しますよね。 )

次のような形です。

#!/usr/bin/env python
import argparse


# パーサーオブジェクトの作成
parser = argparse.ArgumentParser()

# 必要な引数の追加
parser.add_argument("-o", "--out_file")  # nargsを省略しているので1
parser.add_argument("-i", "--in_file", nargs="+")

# 引数の解析
args = parser.parse_args()
print(args.in_file)
print(args.out_file)

# 以下実行結果
$ ./sample.py -o out.txt  -i in1.txt in2.txt in3.txt
['in1.txt', 'in2.txt', 'in3.txt']
out.txt

— in_file の方は複数の結果を受け取れるようにしたので、Python上は配列で結果が来るようになりましたね。

actionによる動作の指定について

actionを使って、オプション引数が存在したときの挙動を指定できます。

デフォルトは store でこれは要するに変数を値に格納するという挙動です。さっきまで見てるのがこれですね。

ただし、Linux/Unixコマンドではこのような値を受け取る引数ばかりではありません。皆さんがよく使う ls コマンドの -l とか -a は別に何か引数を受け取ったりせず、その存在の有無だけが重要ですよね。

この記事の冒頭のコードの `parser.add_argument(“-v”, “–verbose”, action=”store_true”) # on/off フラグ`
もまさにそうで、 -v の有無だけが問題になります。これを実現しているのが、action=”store_true”の部分です。

要するに -v が見つかったら verboseにTrueを格納するよ、という挙動になります。
逆に見つからなかったらFalseが格納されます。

これと逆にオプションがあったらFalseでなかったらTrueになるのが、”store_false”です。

このほか、キーワード引数の登場回数を数えて格納する”count”とか、複数回登場したら結果を都度配列に追加していく”append”などもあります。

これらも一通り公式ドキュメントの一読をお勧めします。
参考: action

ヘルプの作成について

最後にヘルプ機能についてです。自動的に、-h と –helpがヘルプ機能として実装されます。

これはもう実際に試していただくのが一番早いのですが、description等で指定されたプログラムの説明や、受け取れるコマンドライン引数の情報などが表示でき大変便利です。

気をつけないといけないのは、 -h と –help を上書きしないようにすることですね。もちろんどうしてもこれらの引数名を別用途で使いたいとか、自作のヘルプメッセージを実装したいとか事情があれば話は別ですが、普通はデフォルトのヘルプを使った方が良いと思います。

argparseをコマンドライン引数以外の文字列のパースに使う

最後にちょっとマニアックな使い方を紹介します。
このargparseですが、何も指定せずに、 parser.parse_args() すると コマンドライン引数をパースしにいきますが、ここで配列を渡すとその配列をパースします。

sample_str = “-i filename -c 5” みたいな文字列があった時にsample_str.split()して配列に分解して、 parser.parse_args(sample_str.split())と渡すとそれをコマンドライン引数と見立ててパースしてくれるのです。

そんな技術いつ使うねん、と思われるかもしれませんが、僕はマジックコマンドを作る時などに使ってます。
参考: Snowflakeに手軽にSQLを打てるJupyterマジックコマンドを作ってみた|ホンディー | ライフイズテック  (このブログ書いてる人のnote記事です。)

これをやると、parse_args は渡された配列をパースしてるのでコマンドラインから渡した引数は全部無視する点には注意してください

まとめ

長くなりましたが、以上がargparseの説明になります。argparseはシンプルに利用することもできますし、多くの引数を活用して細かいカスタマイズもでき、大変柔軟にツールを作ることができます。 自前ツールを作成する際の大変有益な武器になりますので是非触ってみてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です