スクリプトファイルの構成¶
Python はモジュールのトップレベルから記述を開始できますが、 だらだらと書き進めてしまうと可読性が低くなります。 基本的な構成を統一することでソースコードを共有しやすい状態にしましょう。
前のセクション (トイプログラム - FizzBuzz) からは、次の部分を共通項として抽出できます。
- main 関数の記述
- コマンドライン引数の処理
- shebang の記述、エンコーディングの指定 (PEP 263)
main 関数¶
C言語や Java のように、処理を開始する手続きは main() 関数として独立させましょう。 これは慣習の面と、実際的な面の両方で有用です。 たとえば、Google App Engine の場合は以下のリンクで説明されています。
- アプリケーション キャッシュ - code.google.com
main() を作ったら test() も作るようにしてください。 実際に必要な処理と実験的な処理は分離しましょう。 実装とテストケースを平行して実装することで、 print デバッグからの脱却にも役立ちます。
コマンドライン引数¶
コマンドライン引数はオプションと実引数から構成されます。 オプションはハイフンから始まる文字列で、ハイフンが1つの場合はショートオプション、2つの場合はロングオプションと呼ばれます。 また、値を取るオプションと取らないオプションもあります。 詳しくは Unix 系のコマンドを調べてください。
Python でコマンドライン引数を扱う場合は標準モジュールの argparse を使います。 Python 2.4 系もサポートする場合は optparse を使うことになりますが、その局面は多くないでしょう。
サードパーティーのライブラリとしては docopt と gflags が実際的だと考えられます。 docopt はドキュメントも一緒に書けること、Python 以外のプログラム言語(例えば Go)への移植が容易なことがメリットです。 gflags は複数のモジュールからなるスクリプトを開発する場合に有用です。 その他、Django や Twisted などのフレームワークを利用する場合はオプションライブラリが同梱されています。
- docopt - docopt.org - Command-line interface description language
- python-gflags - code.google.com (更新は止まっています)
オプションの処理¶
コマンドラインスクリプトの場合は、以下の引数をオプションで制御できないかを検討してください。
- help - スクリプトの使い方を表示する
- version - スクリプトのバージョンを表示する
- verbosity - ログ出力の冗長性を高くする
- quiet - ログ出力の冗長性を低くする
- force - 何が何でも実行させる
- dryrun - 実際には実行しない
- configuration - 設定ファイルを指定する
自動プログラミング¶
フルスクラッチでソースコードを記述するのではなく、 何らかの雛型 / スニペットからソースコードを記述するクセを付けましょう。
- 自動プログラミング - wikipepia.org
- Generative Programming - Methods, Tools, and Applications
雛型を共有しておくことにより、エンコーディングの間違いは減少し、 コマンドライン引数に関する最低限のルールは守られます。 また、主要な処理を main 関数に閉じ込めておくことができますので、 スクリプトの再利用性が高まります。
もちろん、パッケージを共有することでもこれらは改善できます。 基本的な記述に時間をかけることなく、関心の高い部分の実装に時間をかけられることが大切です。
宿題¶
自分用のスクリプトテンプレートを用意してください。
以降の章では次の3つを前提として進めます。
- if __name__ == '__main__': で main 関数を呼び出す。
- main 関数から個別の処理を呼び出し、個別の処理は test からも確認可能にする。
- main 関数の最初で引数を処理する。
例 (boilerplate.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""%prog [options] args
"""
import argparse
import logging
def parse_args():
"""Parse arguments and set up logging verbosity.
:rtype: parsed arguments as Namespace object.
"""
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("-f", "--file", dest="filename",
help="setting file", metavar="FILE")
parser.add_argument("-o", "--output", dest="output",
help="output file", metavar="FILE")
parser.add_argument("-n", "--dryrun", dest="dryrun",
help="dry run", default=False, action="store_true")
parser.add_argument("-v", "--verbose", dest="verbose", default=False,
action="store_true", help="verbose mode")
parser.add_argument("-q", "--quiet", dest="quiet", default=False,
action="store_true", help="quiet mode")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
elif not args.quiet:
logging.basicConfig(level=logging.INFO)
return args
def process(args):
"""Main procedure with some tests.
"""
pass
def main():
args = parse_args()
process(args)
def test():
pass
if __name__ == '__main__':
main()
# vim: set et ts=4 sw=4 cindent fileencoding=utf-8 :