はじめに
シン・エヴァ、最優秀賞おめでとうございます!1 nikkieです。
この1月に出た書籍『Pythonエンジニア育成推進協会監修 Python実践レシピ』(以下、『Python実践レシピ』)を読んでいます。
Python標準ライブラリのargparse
について、書籍での説明を補足したいと強く思い、この記事を書きました。
『Python実践レシピ』は、豊富なライブラリを紹介しています。
リファレンス的に読んでいますが、argparse
の取り上げ方については、「情報量が少なく、かえって誤解されてしまうかも」と感じました。
読んだ方が"実践"しやすくなるよう、この記事で勝手に補足します。
おことわり
- nikkieは『Python実践レシピ』の執筆にもレビューにも関わっておらず、この記事は『Python実践レシピ』的には 非公式 情報です(非公式だから、「勝手に 補足」と言っています)
- nikkieが勝手にやっていることではありますが、情報の 正確性は追求 したく、公式ドキュメントなど信頼できると考える情報源をソースにします
用語の定義(公式ドキュメントの翻訳にならっています)
- 位置引数:positional arguments
- オプション引数:optional arguments
目次
- はじめに
- おことわり
- 目次
- この記事で強く伝えたいこと
- Python実践レシピでのargparseの紹介
- 📣位置引数にできる!
- オプション引数から位置引数への書き換えポイント
- オプション引数にrequired=Trueを指定するのは「一般的には悪いやり方」
- type=strは省略できると考えます
- IMO:『Python実践レシピ』のargparseのコードをnikkieが書くなら
- 終わりに
この記事で強く伝えたいこと
『Python実践レシピ』を読んでargparse
を初めて知った方へ
- オプション引数だけではないです。位置引数もパースできます
- コマンドを作る上で必要ないろんなことをカバーしているモジュールです。Python実践レシピにもURLが掲載されていますが、ぜひドキュメントを確認してください
- まずオススメ
argparse
チュートリアル:https://docs.python.org/ja/3/howto/argparse.html - (難しいと感じるかもしれませんが、)より使いこなすために ドキュメント:https://docs.python.org/ja/3/library/argparse.html
- まずオススメ
Python実践レシピでのargparse
の紹介
「Chapter10 汎用OS・ランタイムサービス」の10.4でargparse
が紹介されています。
10.4 コマンドラインオプション,引数を扱う―argparse
例として登場するのは、文字列を繰り返すPythonスクリプトです(10.4.1 コマンドラインオプションを扱う)。
スクリプトへの引数(オプション)として、(1)文字列、(2)繰り返し回数の2つを入力します。
呼び出し方は以下のようになります。
$ # python repeat.py -s 文字列 -n 繰り返し回数 $ python repeat.py -s シオン -n 3 シオンシオンシオン
オプションを指定する順番に意味はないので、python repeat.py -n 3 -s シオン
と指定しても同じ結果が得られます。
繰り返し回数(-n
オプション)として3
を、文字列(-s
オプション)としてシオン
を指定したことには変わりありませんからね。
繰り返し回数の引数は省略できるように実装されています。
省略した場合は2
が指定されたものとします。
$ python repeat.py -s シオン シオンシオン
『Python実践レシピ』は非常に意欲的に、豊富なライブラリを紹介しており、argparse
の例をClick
2で書き換える例も紹介しています。
「紙幅の都合もあり、Python実践レシピでは"オプション"としての用途に振り切って執筆したのかな」と捉えていまして、以下でargparse
について補足していきます。
📣位置引数にできる!
※詳しく知りたい方は、チュートリアルの「位置引数の入門」をご覧ください:
https://docs.python.org/ja/3/howto/argparse.html#introducing-positional-arguments
文字列を1つ目の引数、繰り返し回数を2つ目の引数に取るように書き換えられます。
呼び出し方
$ # python repeat.py 文字列 繰り返し回数 $ python repeat.py シオン 3 シオンシオンシオン
引数を指定する順番に意味をもたせているということです。
書き換える前と同様に、繰り返し回数(2番めの引数)を省略してスクリプトを呼び出すこともできます。
$ python repeat.py シオン シオンシオン
実装
※help
引数で指定するヘルプメッセージは省略しています。
import argparse parser = argparse.ArgumentParser(description="Example command") parser.add_argument("string") parser.add_argument("num", type=int, nargs="?", default=2) args = parser.parse_args() print(args.string * args.num)
書籍のコードから以下のように変更しました。
- オプション引数ではなく、位置引数とする(
add_argument
の第1引数に渡す文字列を変更) - 繰り返し回数は、省略できる位置引数とする(
add_argument
のnargs
引数を利用)
オプション引数から位置引数への書き換えポイント
引数名を-
から始めないことで、位置引数とする
add_argument
の第1引数は-
から始めないことで、位置引数として指定しました。
parse_args() が呼ばれたとき、オプション引数は接頭辞 - により識別され、それ以外の引数は位置引数として扱われます:
https://docs.python.org/ja/3/library/argparse.html#name-or-flags
位置引数はオプション(指定が任意)ではなく、指定が必須になっているため、第1引数stringは、required
引数の指定が不要となります。
残っていると、「TypeError: 'required' is an invalid argument for positionals」が送出されます(「required引数は位置引数には無効」とのこと)。
parser.add_argument("string")
第2引数numは、省略できる位置引数
位置引数としての指定を省略した場合にデフォルト値を使うよう、nargs
引数とdefault
引数を指定しています。
nargs
https://docs.python.org/ja/3/library/argparse.html#nargs'?' -- (略)コマンドライン引数が存在しない場合、default の値が生成されます。
default
https://docs.python.org/ja/3/library/argparse.html#defaultnargs が ? か * である位置引数では、コマンドライン引数が指定されなかった場合 default の値が使われます。
parser.add_argument("num", type=int, nargs="?", default=2)
オプション引数にrequired=Trueを指定するのは「一般的には悪いやり方」
『Python実践レシピ』のコードは、文字列のオプション(-s
)の指定を必須にするためにrequired
引数にTrue
を渡しています。
parser.add_argument("-s", "--string", required=True)
この点について、argparseのドキュメントに「一般的には悪いやり方」という記載を見つけました。
注釈: ユーザーは、通常 フラグ の指定は 任意 であると認識しているため、必須にするのは一般的には悪いやり方で、できる限り避けるべきです。
https://docs.python.org/ja/3/library/argparse.html#required
『Python実践レシピ』の記載だけを読むと、「required=True
と指定すればいいのか」と誤解してしまうかもしれません。
書籍は情報が正確である(例えば、公式ドキュメントに沿っている)ことを私は重視しています。
初めて知る読者が間違った使い方を実践してしまわないよう、『Python実践レシピ』のコードはrequired=True
と書かなくて済むように、位置引数で紹介したほうが適切だったのではないかと考えます。
Zen of Pythonには「Special cases aren't special enough to break the rules. Although practicality beats purity.3」とありますね。
オプション引数でrequired=True
を使うというのは特別扱いだと思います。
どうしても特別扱いしなくてはいけない理由が私には見出だせていないので、ルールを破らず位置引数で実装したいかなという意見です。
type=str
は省略できると考えます
https://docs.python.org/ja/3/library/argparse.html#type
デフォルトでは、ArgumentParser オブジェクトはコマンドライン引数を単なる文字列として読み込みます。
第1引数はデフォルトの挙動で問題ないので、type=str
という指定はしませんでした。
私はint
やfloat
に変えたいとき(文字列として扱われると困るとき)だけ、type
引数を指定しています。
パスを表す文字列をpathlib.Path
に変えることもありますね。
argparseのチュートリアルやドキュメントのコードで、add_argument
にtype=str
が指定されている例はほとんど見ないので、省略してしまっていいのではないかと考えています。
IMO:『Python実践レシピ』のargparse
のコードをnikkieが書くなら
ここで実装を示したように、位置引数を使います。 オプション引数は使いません。
理由は2点あり、どちらも私の好みによるものです。
-s
,-n
というオプションを毎回入力するのが面倒だから(インターフェースに関する好み)- 最初にヘルプメッセージを表示して、位置引数の順番を確認したほうが手っ取り早いと感じます
- オプション引数の指定を必須にするために
required=True
とするのは、ドキュメントによれば一般的に悪いやり方だから(一般的に悪いやり方は採用したくないという好み)
『Python実践レシピ』には引数が1つのケースをオプション引数で実装する例も登場します(10.4.4)が、その場合も私だったら位置引数とします。
終わりに
『Python実践レシピ』で紹介されたargparse
について、オプション引数だけでなく、位置引数もあることを補足しました。
書籍は紙面が限られており、その中で豊富なライブラリを紹介するという『Python実践レシピ』のスタンスは素晴らしいと思います。
そして、この本は認定試験の教科書でもあるため、多くの方が読むと期待されます。
残念ながらargparse
については説明が十分とは感じられず、「情報量が少ないためにargparse
への誤解が広がってはいけない」と勝手に補足しました。
Python公式ドキュメントを翻訳されているcocoatomoさんが、以下のように書いています。
Python公式ドキュメントが取り零している部分は、きっと他のブログや勉強会の発表で解説されるでしょう。
『Python実践レシピ』についても、この記事が補完関係になったらいいなと思っていますし、今後も勝手に補足していきたいと考えています。
(執筆者の方々やレビュアーの方々には顔見知りの方もいらっしゃるので、)私の補足の仕方として至らない点があれば、TwitterやSlackのDMでフィードバックいただければ大変ありがたいです。
-
優秀賞の『アイの歌声を聴かせて』も面白いからみんな観て!(このブログのお約束ですね)↩🏆最優秀アニメーション作品賞に輝いたのは、「シン・エヴァンゲリオン劇場版」でした❗
— 日本アカデミー賞協会 (@japanacademy) 2022年3月11日
おめでとうございます🎊#日本アカデミー45 #第45回日本アカデミー賞 #シン・エヴァンゲリオン劇場版 pic.twitter.com/6rGy6q1ykb -
「Command Line Interface Creation Kit」、略してClickだそうで、コマンドを作るのをサポートする、サードパーティライブラリです(argparseと同じことができると理解しています) https://click.palletsprojects.com/en/8.0.x/↩
-
「特別だと思うものもルールを破るほど特別ではない。ただし、実用に耐えるようにする」(『パーフェクト Python [改訂2版]』より)↩