argparse を試してみた

コマンドライン引数を処理する argparseモジュールを試してみた。argparseモジュールにはすごくたくさんの機能があってとてもじゃないけどなかなか把握しきれないので、今回は基本的なことだけ。Hello, worldプログラムを元にいくつか引数処理を追加してみた。

まずはベースとなるHello, worldプログラムを書いておく。

print 'Hello, world.'
^o^ > python hello_args0.py
Hello, world.

基本的なフロー

基本的には、

  1. パーサーを作る
  2. 引数を追加する
  3. パースする

という流れになる。パーサーは argparse.ArgumentParser。コンストラクタの引数にもいろいろあるけど、とりあえずはプログラムの概要を示すdescription引数だけ与えておけばよさそうだ。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.parse_args()

print 'Hello, world.'

上の例では引数を定義していない。これでも立派にargparse.ArgumentParserは動いてくれる。

^o^ > python hello_args1.py --help
usage: hello_args1.py [-h]

Hello world program.

optional arguments:
  -h, --help  show this help message and exit

このとおり、–helpオプションは勝手に作ってくれる。

位置引数

argparseでは’-‘または’–‘で始まらない引数を位置引数と呼んでいるらしい。普通はオプションとは言わない引数のことだな。で、これを使って world の代わりに任意の名前を指定できるようにしたものがこれ。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store',
                    help='specify name instead "world".')
args = parser.parse_args()

if args.name:
    print 'Hello, %s.' % (args.name)
else:
    print 'Hello, world.'

5,6行目でadd_argumentメソッドを使って位置引数を追加している。’name’は引数の名前、これはあとで引数の値を参照するときの属性名になる。metavar=’NAME’はヘルプメッセージの中で使う文字列、nargs=’?’は引数が無いか1つだけあることを示している。action=’store’は引数を属性にセットすることを指示している。最後のhelp=~はヘルプメッセージに表示する引数の説明だ。

7行目でコマンドライン引数をパースし、args変数に代入。args変数には指定した引数がname属性としてセットされていて、10行目で参照している。

^o^ > python hello_args2.py --help
usage: hello_args2.py [-h] [NAME]

Hello world program.

positional arguments:
  NAME        specify name instead "world".

optional arguments:
  -h, --help  show this help message and exit
^o^ > python hello_args2.py Andy
Hello, Andy.

値をとらないオプション

‘-‘または’–‘から始まるオプションには値をとるオプションととらないオプションがある。まずは値をとらないオプションから。
‘-m’または’–morning’オプションは、Helloの代わりにGood morningと挨拶する。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store',
                    help='specify name instead "world".')
parser.add_argument('-m', '--morning', dest='morning', action='store_true',
                    help='good morning')
args = parser.parse_args()

if args.morning:
    greeting = 'Good morning'
else:
    greeting = 'Hello'

if args.name:
    print '%s, %s.' % (greeting, args.name)
else:
    print '%s, world.' % (greeting)

dest=’morning’はあとから参照するときの属性名を指定している。これを指定しないと長いオプション名から’–‘を取り除いたものが属性名になるらしい。action=’store_true’は属性にTrueをセットすることを指示している。ちなみにaction=’store_false’とすることもできる。

^o^ > python hello_args3.py --help
usage: hello_args3.py [-h] [-m] [NAME]

Hello world program.

positional arguments:
  NAME           specify name instead "world".

optional arguments:
  -h, --help     show this help message and exit
  -m, --morning  good morning
^o^ > python hello_args3.py --morning Andy
Good morning, Andy.

値をとるオプション

値をとるオプションを作るには、action=’store’とする。こうすると属性に値がセットされる。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store',
                    help='specify name instead "world".')
parser.add_argument('-m', '--morning', dest='morning', action='store_true',
                    help='good morning')
parser.add_argument('-t', '--times', dest='times', type=int, action='store', default=1,
                    metavar='N', help='repeat N times')
args = parser.parse_args()

if args.morning:
    greeting = 'Good morning'
else:
    greeting = 'Hello'

for n in range(args.times):
    if args.name:
        print '%s, %s.' % (greeting, args.name)
    else:
        print '%s, world.' % (greeting)

type=’int’ は値が整数であるべきことを指示している。default=1 は –times オプションが指定されなかったときのデフォルト値だ。

^o^ > python hello_args4.py --help
usage: hello_args4.py [-h] [-m] [-t N] [NAME]

Hello world program.

positional arguments:
  NAME             specify name instead "world".

optional arguments:
  -h, --help       show this help message and exit
  -m, --morning    good morning
  -t N, --times N  repeat N times
^o^ > python hello_args4.py --times 3 Andy
Hello, Andy.
Hello, Andy.
Hello, Andy.

値をとらないオプション(2) action=’store_const’

さて、最後にメインルーチンの部分をもっとすっきりさせよう。何でこんなにごちゃごちゃしてるかといえば、if文が2つもあるからだ。
まずひとつは位置引数nameにデフォルト値を設定する。
それからもうひとつは、–morningオプションで設定する値を True から実際に出力する値に変更する。これには action=’store_const’ という、特定の値をセットするアクションが使える。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store', default="world",
                    help='specify name instead "world".')
parser.add_argument('-m', '--morning', dest='greeting', action='store_const',
                    const='Good morning', default='Hello', help='good morning')
parser.add_argument('-t', '--times', dest='times', type=int, action='store', default=1,
                    metavar='N', help='repeat N times')
args = parser.parse_args()

for n in range(args.times):
    print '%s, %s.' % (args.greeting, args.name)

action=’store_const’ とすると、constで指定した値がセットされる。また、オプションが指定されなかった場合には default で指定されている値がセットされる。ここでは const=’Good morning’、default=’Hello’としている。

^o^ > python hello_args5.py Andy
Hello, Andy.

^o^ > python hello_args5.py --morning Andy
Good morning, Andy.

期待通りに動作しているようだ。

その他

argparseにはこのほかにもいろいろな機能があって、たとえばサブコマンドを扱ったりもできる。このエントリでは扱いきれないので、いつか書いてみたい。
なお、詳しくはマニュアルを参照のこと。

cf. http://docs.python.jp/2/library/argparse.html