argparseを使ってみた

追記:
t2yさんが翻訳してくださいました。 argparseコマンドラインオプションと引数の解析


python 2.7からコマンドラインの引数を解釈するargparseモジュールが標準で入りました。

従来使われてきたoptparseは廃止されます。ただし、広く使われているところから他の廃止されるモジュールとは異なり消されることはなく、警告も標準では出ません。

optparseからargparseに移行する理由や廃止の進め方はPEP 389に記載されています。
以下にPEP 389の和訳を(訳に自信がない場所がありますが)行ないましたので参考にしてください。
http://www.tdoc.info/PEP-ja/389.html

というわけで、ざっとargparseの使い方を勉強したのでここに公開します。間違ってたらご指摘ください。

なお、ちゃんとしたドキュメントは http://docs.python.org/library/argparse.html にあります。

argparseの大まかな流れ

  • 1. parserを作ります
  • 2. 解釈する引数を付け加えます
  • 3. parseします
  • 4. enjoy!

例 その1

# -*- coding: utf-8 -*-

import argparse

parser = argparse.ArgumentParser(description='簡単な例です') # parserを作る
parser.add_argument('bar')         # 引数を追加します
parser.add_argument('-f', '--foo') # オプションを追加します
parser.add_argument('-r', required=True) # このオプションは必須です
parser.add_argument('--version', action='version', version='%(prog)s 2.0') # version
args = parser.parse_args() # コマンドラインの引数を解釈します

print(args)

実行してみましょう。

% python test.py --help
usage: test.py [-h] [-f FOO] -r R bar

簡単な例です

positional arguments:
  bar

optional arguments:
  -h, --help         show this help message and exit
  -f FOO, --foo FOO
  -r R
  --version          show program's version number and exit
% python test.py --version                                 
test.py 2.0
% python test.py hoge                
usage: test.py [-h] [-f FOO] -r R bar
test.py: error: argument -r is required
% python test.py -r Required hoge                               
Namespace(bar='hoge', foo=None, r='Required')

例 その2 ヘルプを出す

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--foo', help='foo help')
args = parser.parse_args()

add_argumentにhelpを付け加えます。そうすると、--helpを付けて起動するとこのように表示されます。

% python test2.py --help
usage: test2.py [-h] [--foo FOO]

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

例 その3 いろいろな変数指定

add_argument()にnargsというパラメータを渡すと可変長引数を実現できます。

複数の引数

integerの数字を指定するとその数の引数を要求するようになります。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs=2)
parser.add_argument('bar', nargs=1)
args = parser.parse_args()

print(args)

これはこうなります。

% python test3.py
usage: test3.py [-h] [--foo FOO FOO] bar
test3.py: error: too few arguments
% python test3.py bar    
Namespace(bar=['bar'], foo=None)
% python test3.py bar --foo hoge                    
usage: test3.py [-h] [--foo FOO FOO] bar
test3.py: error: argument --foo: expected 2 argument(s)
% python test3.py bar --foo hoge hige 
Namespace(bar=['bar'], foo=['hoge', 'hige'])
オプション引数

'?'を指定するとあってもなくても良くなります。
これは例えば入出力ファイルの指定で指定がなければ標準入出力を指定する、などに使えます。

parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout)

こうなります。

% python test3-2.py --help  
usage: test3-2.py [-h] [infile] [outfile]

positional arguments:
  infile
  outfile
% python test3-2.py
Namespace(infile=<open file '<stdin>', mode 'r' at 0x100299150>, outfile=<open file '<stdout>', mode 'w' at 0x1002991e0>)
% python test3-2.py hoge 
Namespace(infile=<open file 'hoge', mode 'r' at 0x100450b70>, outfile=<open file '<stdout>', mode 'w' at 0x1002991e0>)
% python test3-2.py hoge hoge
Namespace(infile=<open file 'hoge', mode 'r' at 0x100450b70>, outfile=<open file 'hoge', mode 'w' at 0x100655300>)

argparse.FileTypeの効果により、すでにopenされた状態でinfileとoutfileに格納されています。

可変長引数

'*'を指定すると可変長引数を指定できます。(例は import など省略しています)

parser.add_argument('--foo', nargs='*')
parser.add_argument('--bar', nargs='*')
% python test3-3.py --help
usage: test3-3.py [-h] [--foo [FOO [FOO ...]]] [--bar [BAR [BAR ...]]]

optional arguments:
  -h, --help            show this help message and exit
  --foo [FOO [FOO ...]]
  --bar [BAR [BAR ...]]
% python test3-3.py --foo 1 2 3 --bar A B C
Namespace(bar=['A', 'B', 'C'], foo=['1', '2', '3'])

'*'の代わりに'+'を使うと指定が一つもない場合にエラーメッセージが表示されるようになります。

例 その4 サブコマンド

ArgumentParser.add_subparsers()を使うと、サブコマンドを定義できるようになります。

# -*- coding: utf-8 -*-

import argparse

parser = argparse.ArgumentParser()

# トップレベルのparserを作ります
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help')

# a というサブコマンドのparserを作ります
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('aaa', type=int, help='aaa help')

# b というサブコマンドのparserを作ります
parser_b = subparsers.add_parser('b', help='b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')

args = parser.parse_args() 

print(args)
% python test4.py  --help
usage: test4.py [-h] [--foo] {a,b} ...

positional arguments:
  {a,b}       sub-command help
    a         a help
    b         b help

optional arguments:
  -h, --help  show this help message and exit
  --foo       foo help
% python test4.py a 11      
Namespace(aaa=11, foo=False)
% python test4.py b --baz X 
Namespace(baz='X', foo=False)

全然まだまだこんなもんじゃない

説明してませんが、例4にあるように引数を数値に限定したり、指定の中から選ばせたりもできます。他にも、値を格納する場所を指定したり、それにつれてアクションを起こしたり、ファイルをいきなり開いたり(例3でありました)、相反するオプションを指定されたときに解決する方法を書けたりと、ほんとありとあらゆることができます。さらに自分で拡張をすることもできます。

もはやoptparseを使う理由はありません。argparse、そしてそれを標準で使えるpython 2.7にすぐに移行しましょう(とか言ってみる)