Python の引数処理モジュールである argparse を使ってみます。 そろそろ Python 3.3 で作業を始めるべきでしょうし、いつまでも Python 2.4 のことを考えていても窮屈なためです。
Python の標準ライブラリを使った引数処理には、次のような段階があると思います。
- sys.argv を使った原始的な方法
- getopt モジュールを使った Unix 系の伝統的な方法
- optparse モジュールを使った Python 2.4 などもターゲットにした方法
- argparse モジュールを使った最近の方法
Argparse Tutorial には次のように記載されています。
There’s two other modules that fulfill the same task, namely getopt (an equivalent for getopt() from the C language) and the deprecated optparse. Note also that argparse is based on optparse, and therefore very similar in terms of usage.
ざっくり日本語訳 :
同じタスクを満足にこなすためには他にも2つのモジュールがあります。 C 言語の getopt() と等価な getopt と、非推奨になった optparse です。 argparse は optparse をベースにしていますので、使い方は非常に似ています。
その他にもサードパーティのライブラリを使う方法があります。 何らかのフレームワークに付随するものや gflags を使う方法はこちらの記事で少しずつ触れています。
"There should be one-- and preferably only one --obvious way to do it." (* Zen of Python) という精神の通りにはいきませんが、 やはり標準ライブラリに沿って実装する方法を身につけておくことは大切だと思いますので、 argparse を使います。
サンプルスクリプト
早速サンプルスクリプトです。 次の機能を利用しています。
- 引数処理オブジェクトの初期化
- オプション引数の設定 (year, month, day, days, format)
- フラグ引数の設定 (reverse)
- 出力ファイルの設定 (output)
- 排他的引数の設定 (verbose, quiet)
- 実際の引数処理
import argparse import datetime import logging import sys TODAY = datetime.date.today() def main(): parser = argparse.ArgumentParser() parser.add_argument('--year', type=int, default=TODAY.year) parser.add_argument('--month', type=int, default=TODAY.month) parser.add_argument('--day', type=int, default=TODAY.day) parser.add_argument('--format', default='%Y%m%d') parser.add_argument('--days', type=int, default=1) parser.add_argument('--reverse', default=False, action="store_true") parser.add_argument('--output', type=argparse.FileType('w'), default=sys.stdout) group = parser.add_mutually_exclusive_group() group.add_argument("-v", "--verbose", dest="verbose", default=False, action="store_true", help="set logging to verbose mode") group.add_argument("-q", "--quiet", dest="quiet", default=False, action="store_true", help="set logging to quiet mode") args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) elif not args.quiet: logging.basicConfig(level=logging.INFO) s = datetime.date(args.year, args.month, args.day) logging.debug("Start: %s", s) for i in xrange(args.days): if args.reverse: t = s - datetime.timedelta(days=i) else: t = s + datetime.timedelta(days=i) args.output.write(t.strftime(args.format)) args.output.write('\n') if __name__ == '__main__': main()
スクリプトの実行
引数無しで実行すると、その日の年月日を出力します。
$ python date-expand.py 20121105
month や day で出力する日付を設定できます。
$ python date-expand.py --month=10 --day=1 20121001
format オプションに日付書式を与えるとフォーマットが変わります。 strftime() が受け付けられないものはエラーになります。
$ python date-expand.py --format="%Y-%m-%d" 2012-11-05
days オプションでその日数分の日付文字列を出力します。 整数以外を与えると引数解析の時点でエラーになります。
$ python date-expand.py --days=7 20121105 20121106 20121107 20121108 20121109 20121110 20121111
action="store_true" を指定するとフラグになります。
$ python date-expand.py --days=7 --reverse 20121105 20121104 20121103 20121102 20121101 20121031 20121030
output で出力ファイルを指定できます。 argparse.FileType('w') を指定することで、自動で書き込みストリームを開いておいてくれます。 存在するディレクトリを指定するとエラーになるなど、ちょっとエラー処理が役に立ちます。 また、デフォルト値に sys.stdout を指定しておくと、標準出力とファイル出力を透過的に扱えます。
$ python date-expand.py --month=9 --day=1 --days=30 --output date.dat $ head date.dat 20120901 20120902 20120903 20120904 20120905 20120906 20120907 20120908 20120909 20120910
ログ出力を冗長なレベルに設定します。 verbose と quiet は排他的に設定しておきたいので add_mutually_exclusive_group() を使います。 上記のソースコードでは parser.add_argument() ではなく group.add_argument() と呼び出します。 また、オプションの名称はロングオプションだけでなくショートオプションを指定できることも分かります。 (他のオプションも同様)
$ python date-expand.py -v DEBUG:root:Start: 2012-11-05 20121105
リファレンスなど
まずは 公式ドキュメント を読みたいところですが、パッと見で難解なコード例になっていると思います。 そこで、公式サイトのチュートリアル - Argparse Tutorial - から読み進めるのが分かりやすいでしょう。
また、Python Module Of The Week には "parents" を使った階層化の例があります。 それ以外にも nargs を指定して引数の数を明示する方法、カスタムアクションを実行する方法などがあります。 一通り読んでみると発見が多いと思います。
終わりに
Python の引数処理モジュールである argparse を使ってみました。 argparse の利点は、Python 2.7 と Python 3.x の両方で動作することです。 そろそろ Python 2.4 から Python 2.6 をサポートしないライブラリも登場してきていますし、 よっぽどなユーザーベースがあるコードでない限りは argparse に移行した方が良さそうです。
あと、上記のようなコードを毎回記述するのも冗長なので、 clitool としてモジュールにまとめてみました。 clitool.cli.parse_arguments() と呼び出すことで入出力に関するオプションを設定してくれます。
- clitool - pypi.python.org
引数処理以外にもお勉強がてら簡単な処理をまとめていますので、ちょっとしたツールを実装するには便利だと思います。
0 件のコメント:
コメントを投稿