Python プログラムを書く前に
書くからには出来るだけちゃんとしたのを書きたいよね。
(Python の理念的にも)
というわけで書き方の作法を頭にいれておく。
みんな大好きコーディング規則
日本語 PEP - PEP 8 -- Style Guide for Python Code
インデント強制よりこっちのが特徴的な気がする docstring
日本語 PEP - PEP 257 -- Docstring Conventions
プログラマの為に assert
Python リファレンスマニュアル - 6.2 Assert 文 (assert statement)
自動ドキュメント生成 pydoc
Python ライブラリリファレンス - 5.1 pydoc -- ドキュメント生成とオンラインヘルプシステム
docstring からテストを実行する doctest
Python ライブラリリファレンス - 5.2 doctest -- 対話モードを使った使用例の内容をテストする
コメントの書き方が上達しそうな言語だよ Python 。
後でもうちょっとまとめる。
(追記)書いた。
自分にとって重要そうなとこを抜き出し。
コーディング規則一般
- インデントは 1 段スペース 4 つ、 1 行は 79 文字。
- 英語を母国語としない Pythonプログラマの方々へ。あなたの母国語を話さない人が、そのソースコードを決して読まないことを、120%確信できる場合を除いて、コメントは英語で書いていただきたい。(日本語訳より抜粋)
- パッケージ名、モジュール名は lowercase (_ を含まない)
- クラス名、例外名は CapWords
- グローバル変数、関数は lowercase (_ を含んでもよい)
- インスタンスのプロパティは lowercase (_ を含んでもよい) 。ただし、(そういうアクセス権限は言語にないけど) public は先頭に _ をつけない、 private は __ を先頭につける。protected は特に明記はない。
- __ で始まり __ で終わる (__name__ のような) 名前は言語で使われる可能性があるので、避けられるなら避けておく。
docstring
- パッケージ、モジュール、関数、クラス、メソッドにつける。
- 三連二重引用符 (""" もしくは r""" ) を使う。
- モジュールのグローバルに __author__ __credits__ __date__ __version__ があると pydoc で認識してくれる。
パッケージレベル
- パッケージの __init__.py のモジュールレベル docstring のこと。
- 提供するモジュール、サブパッケージを 1 行概要 とともに列挙。
モジュールレベル
- (module).py の先頭の docstring のこと。
- 提供するクラス、例外、関数などを 1 行概要とともに列挙。
- スタンドアロンのスクリプトの場合、この docstring は -h が渡されたときに出力されるものと同一であるべき。数画面分いっぱいにわたるぐらい、詳細に書く。
日本語訳だと
スクリプト (スタンドアロンのプログラム) の docstring は "使用法" メッセージとして使うことができ,スクリプトが間違った引数や引数なしで起動された場合 (あるいは "ヘルプ" を表す "-h" オプションで起動された場合) に出力されます.
http://www.python.jp/doc/contrib/peps/pep-0257.txt
のようにあって、自動でそんなことやんのか!?と思ってしまったけど、そんなことはない。 optparse モジュールもそこまではやらない。
原文だと
The docstring of a script (a stand-alone program) should be usable as its "usage" message, printed when the script is invoked with incorrect or missing arguments (or perhaps with a "-h" option, for "help").
訳: ぼく
http://www.python.org/dev/peps/pep-0257/#multi-line-docstrings
スクリプト (スタンドアロンのプログラム) の docstring は、
"-h をつけたり引数が間違っていたりしたときに出るような 'usage'" として使える (ように書かれている) べきである。
くらいのノリ。
クラスレベル
- 動作の概説。
- public を列挙。
- protected を public とは分けて列挙。
- コンストラクタについては __init__ に書く。
- 他クラスの子クラスである場合、その差を明記する
- 親クラスのメソッドを子クラスで再定義し、かつ子クラスのメソッド中から親クラスのメソッドを呼ばない場合、語句 override を使う。
- 親クラスのメソッドを子クラスで再定義し、かつ子クラスのメソッド中から親クラスのメソッドを呼ぶ場合、語句 extend を使う。
関数、メソッドレベル
- 動作の概説。
- 引数、戻り値、副作用、発行される例外、呼び出せる状況への制限を (それぞれ存在する場合) 記述する。
- オプションの引数も示すべき。
- キーワード引数がインタフェースの一部となっているかどうかも記述する。
assert
日本語でいうと表明。
"契約プログラミング" または "契約による設計" (Design By Contract, DbC と略す) という考え方のためのもの。
wikipedia:表明
wikipedia:契約プログラミング
単純にいうと、ある条件が偽になったら 例外 AssertionError を投げる。
def check_password(password): assert type(password) is str, "プログラマへ。パスワードが文字列じゃないってなにごと?" if len(password) < 4: raise Exception("パスワードが短すぎるよ!")
check_password([1, 2, 3]) # 結果: # Traceback (most recent call last): # File "/path/to/script", line X, in <module> # check_password([1, 2, 3]) # File "/path/to/script", line Y, in check_password # assert type(password) is str # AssertionError: プログラマへ。パスワードが文字列じゃないってなにごと?
check_password("abc") # 結果: # Traceback (most recent call last): # File "/path/to/script", line X, in <module> # check_password("abc") # File "/path/to/script", line Y, in check_password # raise Exception("パスワードが短すぎるよ!") # Exception: パスワードが短すぎるよ!
この場合、 check_password は
- password は文字列を受け取ることを前提としている。 docstring には str password とか書かれているレベル。
- password が短すぎたら例外を投げてあげる。
- パスワードが十分長ければ何もしない。
という仕様と解釈できる。
こういった関数を使う場合、 password に文字列以外を渡すのは、明らかに使う側のプログラム、呼び出した部分を書いたプログラマのミス。だって check_password はそういう仕様だから。
そういったプログラム自体のバグと、ユーザの利用によって発生するエラーを切り分けるために assert を使う。
このような性質のため、assert にはこんな特徴がある。
- コンパイル時にオプションを渡すだけで assert を無効 (チェックをしない) にし、実行速度を向上させることができる。
- 十分なデバッグを行い、プログラム自体のバグがなくなったと判断できたら、リリース用に assert を無効にしても良い。
- ソースコードを修正することなく、オプション 1 つでバグチェック機能をオン・オフできるのは非常に楽。
うまく説明できてそうにないので、良い感じに assert を実装している D 言語のリファレンスへのリンク。
契約プログラミング - プログラミング言語 D 2.0
やっぱ in out body はいいなぁ。まさにソースがドキュメント。
doctest
#/path/to/test.py def foo(): """常に 3 を返す テスト用の関数です。 常に 3 を返します。 >>> foo() 3 >>> foo() 2 ↑テストコード。 """ return 3 if __name__ == "__main__": import doctest doctest.testmod()
$ python /path/to/test.py -v Trying: foo() Expecting: 3 ok Trying: foo() Expecting: 2 ********************************************************************** File "/path/to/test.py", line X, in __main__.foo Failed example: foo() Expected: 2 Got: 3 1 items had no tests: __main__ ********************************************************************** 1 items had failures: 1 of 2 in __main__.foo 2 tests in 2 items. 1 passed and 1 failed. ***Test Failed*** 1 failures.
docstring に書かれているとおり。非常に分かりやすい。
悪い例を見せるためでなく、正しい例を見せるためなので、通常は失敗するテストは書かない。
docstring いいわー。
これであと interface があって interface に assert in assert out が書けて public/protected/private があればなー。