4.8. 全てをまとめる

コードの最後の行はまだ解体していない唯一の行なのですが, それは全ての仕事をしている行です. しかし必要なものはもうきっちり準備してあるので, 仕事は簡単です. 全てのドミノは配置されていて, 今こそ倒す時です.

ここが apihelper.py の要です.

    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])

これは複数行に分割された 1 つのコマンドですが, 行継続文字 (\) を使っていないことに注意してください. 私がある式はバックスラッシュを使わずに複数行に分割することができると言ったのを覚えていますか? リストの内包表記は, 式全体が角括弧で囲われているので, そのように書ける式の 1 つです.

さぁ, 後ろから順に前に向かって解体していきましょう.


for method in methodList

を見ると, この式はリストの内包表記だと分かります. 知っての通り, methodList は object の 対象にしている全てのメソッドのリストです. なので method に値を代入しながらリストを順々に辿ることになります.

Example 4.22. 動的に doc string を取得する

>>> import odbchelper
>>> object = odbchelper                   1
>>> method = 'buildConnectionString'      2
>>> getattr(object, method)               3
<function buildConnectionString at 010D6D74>
>>> print getattr(object, method).__doc__ 4
Build a connection string from a dictionary of parameters.

    Returns string.
1 info 関数では, object はヘルプ情報を取得されるオブジェクトで, 後で引数として渡されます.
2 methodList を順々に辿っていきますが, method は現在のメソッドの名前です.
3 getattr 関数を使うことで, object モジュールの method 関数への参照を取得します.
4 さぁ, もうメソッドの実際の doc string を出力するのは簡単ですね.

次のパズルのピースは doc string を囲う str 関数の使用です. 思い出したと思いますが, str 関数はデータを文字列へ変換する組み込み関数です. しかし doc string は必ず文字列なのに, 何故わざわざ str 関数を使うのでしょうか? その答えは, 全ての関数が doc string を持っているわけではなく, その場合 __doc__ 属性は None になるからです.

Example 4.23. 何故 doc string に str 関数を使うのか?

>>> def foo(): print 2
>>> foo()
2
>>> foo.__doc__     1
>>> foo.__doc__ == None 2
True
>>> str(foo.__doc__)    3
'None'
1 doc string を持たない関数は簡単に定義できて, その関数の __doc__ 属性は None になります. 混乱するかもしれないが, __doc__ 属性を直接評価すると Python IDE は何も出力しません. 何か理屈を考えつくことはできるかもしれませんが, それでもやはり役には立ちません.
2 直接比較することで __doc__ 属性の値が実際に None であると確かめられます.
3 str 関数は null 値を受け取ると, その文字列による表現 'None' を返します.
Note
SQL では, null 値との比較に = NULL ではなく IS NULL を使わなければいけません. Python では, == None も is None も使えますが is None の方が高速です.

さて文字列が得られる保証もできたので, その文字列を processFunc に渡せます. この関数は既に定義した関数で, ホワイトスペースを折り畳むかそのままで何もしないかのどちらかをします. 何故 str 関数を使って None 値を文字列表現に変換するのが重要なのかもう分かりましたね. processFunc は引数に文字列が入っていると仮定して, その split メソッドを呼びますが, もし None を渡した場合は split メソッドは持っていないので呼び出しに失敗します.

さてもっと前に戻って話を続けると, processFunc の返り値と method の ljust メソッドの返り値をフォーマット文字列を使って連結していますね. ljust はまだ見たことのない文字列のメソッドです.

Example 4.24. ljust の紹介

>>> s = 'buildConnectionString'
>>> s.ljust(30) 1
'buildConnectionString         '
>>> s.ljust(20) 2
'buildConnectionString'
1 ljust は与えられた長さになるまでスペースを埋めます. info 関数は出力を 2 つの列にし, 2 列目の doc string を全て揃えるために, これを使っています.
2 与えられた長さが文字列の長さより小さかった場合には, ljust は単に文字列をそのまま返します. 文字列を切り詰めることはありません.

これでもうほとんど終わりです. ljust メソッドからスペース埋めしたメソッド名が与えられ, processFunc 関数の呼び出しからは (もしかしたら折り畳まれているかもしれない) doc string が与えられ, その 2 つを連結し 1 つの文字列にします. methodList のマップ操作を行っているので, 文字列のリストが返ってきます. 文字列 "\n" の join メソッドを使って, このリストの各要素が別々の行にくるような 1 つの文字列に結合し, その結果を出力しています.

Example 4.25. リストの出力

>>> li = ['a', 'b', 'c']
>>> print "\n".join(li) 1
a
b
c
1 これはリストを扱っているときに役立つデバッグの技でもあります. そして Python では, 常にリストとともに作業をしています.

ここまではパズルの最後のピースです. もうこのコードは理解できるはずです.

    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])