Tech Horse Club

技術ブログ×競馬予測

Pythonスクリプトの引数を柔軟に処理する(argparseの使い方)

Pythonを使ったスクリプトに対する引数の取得方式として、基本的にはsys.argvを利用するのが一番シンプルだと思います。しかし、今回新しいスクリプトを作る際に、

  • 引数を細かく指定したい
  • 引数によって、動作仕様を変更したい
  • 任意入力の引数のデフォルト値を定義したい
  • 引数の順序に依存させたくない

といった要件が出てきて、ちょっとどう実装するかイメージが沸きませんでした。。そこで、他に引数を処理する方式がないか調べてみた所、コマンドライン引数をを解析するPythonの標準ライブラリargparse使うことで、やりたいことが実現できそうでしたので、もう少し細かく調べて、実際にスクリプトを実装してみました。

argparseの基本的な使い方

argparseを利用する基本的な流れは下記のとおりです。

  • ArgumentParserオブジェクトを生成する
  • Parserが解釈する引数の定義を追加する
  • 引数を解析する(コマンドラインの解析結果から、Namespaceオブジェクトを生成する)
  • 解析結果を利用する。

というわけで、公式レファレンスを参考に、簡単なスクリプトを書いてみました。

from argparse import ArgumentParser

# ArgumentParserオブジェクトを生成する
parser = ArgumentParser(description='Process some integers.')

# Parserが解釈する引数の定義を追加する
### オプション引数を指定する(-がある時)
parser.add_argument('-hn','--horsename', help='horse name' ,default='キタサンブラック')
### 位置引数を指定する(-が無い時)
parser.add_argument('RaceNo', help='Race No')

# 引数を解析する(コマンドラインの解析結果から、Namespaceオブジェクトを生成する)
args = parser.parse_args()

print(args)

実行結果は下記の通り。-hオプションをつけてスクリプトを実行すると、usageを生成して表示してくれるという便利な機能が標準で有効化されています。

$ python get_argv.py -h
usage: get_argv.py [-h] [-hn HORSENAME] RaceNo

Process some integers.

positional arguments:
  RaceNo                Race No

optional arguments:
  -h, --help            show this help message and exit
  -hn HORSENAME, --horsename HORSENAME
                        horse name
$ python get_argv.py 11
Namespace(RaceNo='11', horsename='キタサンブラック')
$ python get_argv.py -hn サトノダイヤモンド 11
Namespace(RaceNo='11', horsename='サトノダイヤモンド')

実用例

基本的な使い方はわかったので、もう少し実用的な引数解釈処理を作ってみます。今別に作成しているスクリプトで、必要な引数処理の要件をまとめてみました。

  • 日付を指定(必須)
  • 競馬場名を指定(任意、実在する競馬場名のみ指定可能)
  • レース番号指定(任意、デフォルトだと11、複数のレース番号を指定可能、1〜12のみ指定可能)
  • 出力モード(任意、デフォルトの値を指定、準備しているモードのみ指定可能)

上記の要件を満たす引数処理スクリプトを書いてみると、こんな感じになりました。

from argparse import ArgumentParser

# ArgumentParserオブジェクトを生成する
parser = ArgumentParser(description='Process some integers.')

# Parserが解釈する引数の定義追加
## 日付(必須)
parser.add_argument('-d','--date', help='date(mmdd)', required=True)
## 競馬場名(必須・入力値制限)
parser.add_argument('-c','--race_course', help='Race Course',choices=['札幌','函館','福島','新潟','東京','中山','中京','京都','阪神','小倉'])
## レース番号(任意・デフォルト値11・複数指定可能・入力値制限)
parser.add_argument('-n','--race_no', help='Race No',nargs='*',default=[11], type=int, choices=range(1, 13))
## 出力モード(任意・デフォルト値S・入力値制限)
parser.add_argument('-m','--output_mode', help='OutputMode S:stdout D:dataframe',default='S',choices=['S','D'])

# 引数を解析する(コマンドラインの解析結果から、Namespaceオブジェクトを生成する)
args = parser.parse_args()

print(args)

実行例はこんな感じ。

$ python get_argv2.py -d 0903
Namespace(date='0903', output_mode='S', race_course=None, race_no=[11])

$ python get_argv2.py -d 0903 -c 札幌
Namespace(date='0903', output_mode='S', race_course='札幌', race_no=[11])

$ python get_argv2.py -d 0903 -c ほげ
usage: get_argv2.py [-h] -d DATE [-c {札幌,函館,福島,新潟,東京,中山,中京,京都,阪神,小倉}]
                    [-n [{1,2,3,4,5,6,7,8,9,10,11,12} [{1,2,3,4,5,6,7,8,9,10,11,12} ...]]]
                    [-m {S,D}]
get_argv2.py: error: argument -c/--race_course: invalid choice: 'ほげ' (choose from '札幌', '函館', '福島', '新潟', '東京', '中山', '中京', '京都', '阪神', '小倉')

$ python get_argv2.py -d 0903 -c 札幌 -n 1 2 3
Namespace(date='0903', output_mode='S', race_course='札幌', race_no=[1, 2, 3])

$ python get_argv2.py -d 0903 -c 札幌 -n 0
usage: get_argv2.py [-h] -d DATE [-c {札幌,函館,福島,新潟,東京,中山,中京,京都,阪神,小倉}]
                    [-n [{1,2,3,4,5,6,7,8,9,10,11,12} [{1,2,3,4,5,6,7,8,9,10,11,12} ...]]]
                    [-m {S,D}]
get_argv2.py: error: argument -n/--race_no: invalid choice: 0 (choose from 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

$ python get_argv2.py -d 0903 -c 札幌 -n 11 -m D
Namespace(date='0903', output_mode='D', race_course='札幌', race_no=[11])

引数エラーとなるべき所はちゃんとエラーになっていますし、想定通りの引数処理が行われているようです。

簡単な引数処理スクリプトを書いてみて、argparseがとても柔軟に引数の解釈をしてくれることがわかりました。公式を見てみると、サブコマンドを用意したり出来るみたいです。sys.argvよりもこちらを使った方が、柔軟かつ便利な引数処理を実装できそうですね。