えー、さっぱり人気のない、このシリーズの続きをいってみたいと思います(実質的には、第2回の続き)。
関数bencode('spam')は4:spam(bencodeされた値)を返します。
関数、bencodeの定義は(#以降はコメント):
def bencode(x):
r = [] # 空のリストを作る。
encode_func[type(x)](x, r) # xの型を調べ、encode_funcディクショナリをルックアップ。対応する関数へマッピング。その関数を引数(x, r)で呼ぶ。
return ''.join(r) # リストrを結合し、文字列を返す。
引数が'spam'の場合、type(x)はStringTypeとなり(事前にfrom types import StringTypeしてる)、encode_string関数が呼ばれる。
関数、encode_stringの定義は:
def encode_string(x, r):
r.extend((str(len(x)), ':', x))
プロトコル仕様そのまんまなので、説明しません。
注目すべきは、関数ディクショナリの生成部分。
encode_func = {}
....
encode_func[StringType] = encode_string
....
x.bとあった場合、なんでもかんでも、xオブジェクトへの、bという名前のメッセージ・センディングね、と思っちゃいがちですが、Pythonでは、内部的には、xディクショナリをキーbでルックアップしてるだけみたい。通常、そこまで意識する必要ないし、結果も同じだけど、プログラム構築のヒントになるのでは?基本要素の構成法(考え方)を、プログラム全体の構成法にも反映させると言いますか。。。
Rubyではクラスを拡張し、そのオブジェクトの型を調べて処理の分岐をさせる。Pythonでは上記のような関数ディクショナリを作ることにより処理の分岐をおこなうのがエレガントなのではないか?と思うわけです。PythonにはSwitchステートメントがないしね。