きっかけ
はい、「プログラマー脳〜優れたプログラマーになるための認知科学に基づくアプローチ〜」という本を読んでみて、コードリーディングの能力を高めたいなと思っていて、まずは実践。
実際に紹介されていたのは、“コードを読む力”を育てるために、自分の好きな言語でコードを要約してみようというアプローチを試してみます。
今回が第2回です。

本日のテーマは、みんな大好きurllibから、build_opener()さんです。
cpython/Lib/urllib/request.py at v3.14.0b3 · python/cpython · GitHub
def build_opener(*handlers):
"""Create an opener object from a list of handlers.
The opener will use several default handlers, including support
for HTTP, FTP and when applicable HTTPS.
If any of the handlers passed as arguments are subclasses of the
default handlers, the default handlers will not be used.
"""
opener = OpenerDirector()
default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
HTTPDefaultErrorHandler, HTTPRedirectHandler,
FTPHandler, FileHandler, HTTPErrorProcessor,
DataHandler]
if hasattr(http.client, "HTTPSConnection"):
default_classes.append(HTTPSHandler)
skip = set()
for klass in default_classes:
for check in handlers:
if isinstance(check, type):
if issubclass(check, klass):
skip.add(klass)
elif isinstance(check, klass):
skip.add(klass)
for klass in skip:
default_classes.remove(klass)
for klass in default_classes:
opener.add_handler(klass())
for h in handlers:
if isinstance(h, type):
h = h()
opener.add_handler(h)
return opener
導入:build_opener()、お前いったい何者だ
def build_opener(*handlers):
はいはい、opener
をビルドする関数ね。もう名前が仕事してる。
そして引数はお決まりの*handlers
。これは、Python界隈でおなじみの「まとめて持ってきていいよ」ってやつです。
つまり、build_opener(AHandler, BHandler)
みたいに、好きなだけhandlerを渡せるわけだ。
OpenerDirector()、いきなり登場の大物感
opener = OpenerDirector()
急に現れるOpenerDirector
という謎の人物。
名前からしてただ者ではない感がある。なんだろう、映画の監督?それとも起業家?
たぶん「開く処理を統括する偉い人」なんだろうなという理解で先に進みます(こういうの、深追いすると沼る)。
ハンドラー軍団、ぞろぞろ集合
default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
HTTPDefaultErrorHandler, HTTPRedirectHandler,
FTPHandler, FileHandler, HTTPErrorProcessor,
DataHandler]
この関数、何やらデフォルトで使われる「handlerたち」を一列に並べてます。
まるで「開通式に出席する来賓一覧」みたいな顔ぶれ。
- HTTP
- FTP
- プロキシ
- エラー処理
- リダイレクト処理
- 不明な何か(Unknown!)
このメンツだけで相当仕事回せそうな感じ。さすがPython。
hasattr():属性の存在確認をする探偵
if hasattr(http.client, "HTTPSConnection"):
default_classes.append(HTTPSHandler)
ここで登場するのがhasattr()
。名前だけ聞くと「ハサミで切る関数かな?」って思ったけど、全然違った。
これは「そのオブジェクトがその属性を持ってるか?」を調べる便利関数。http.client
に HTTPSConnection
がある=つまり「HTTPS通信できる環境かどうか」を確認して、できるなら HTTPSHandler
を追加!
いや〜、気が利くなこの関数。エスコート上手かよ。
klassとcheckの謎の会話劇が始まる
skip = set()
for klass in default_classes:
for check in handlers:
if isinstance(check, type):
if issubclass(check, klass):
skip.add(klass)
elif isinstance(check, klass):
skip.add(klass)
見た瞬間、「え、何してんのこれ?」ってなるやつ。
まずklass
って名前、完全にclass
のスペルミスだけど、Python的には予約語だから使えないんだよね。代わりにklass
。ちょっとかわいい。
で、これは何してるかというと、
「もしユーザーが独自のhandlerを渡してきて、それがデフォルトのhandlerを置き換えられるなら、デフォルトの方は引っ込んでくれ」
という、非常に空気を読むコードなんです。
これぞPythonの美学。自己主張しない、だけどちゃんと働く。素敵。
skipに入った彼らを除外します
for klass in skip:
default_classes.remove(klass)
もうね、冷たい感じでバサッと切ってます。「お前、ユーザーのhandlerと被ってたよね?退場で。」
容赦ない。けど合理的。やっぱり現場は戦場です。
そして、淡々とhandlerを登録
for klass in default_classes:
opener.add_handler(klass())
for h in handlers:
if isinstance(h, type):
h = h()
opener.add_handler(h)
return opener
ここはクライマックス。
- デフォルトのhandlerたち(被ってなかった組)を登録し、
- ユーザーが渡してきたhandlerも登録し、
- 最後に
opener
を返す!
まさに、handlerたちの夢の共演が完成する瞬間です。
劇場版「urllib.request」公開、って感じ。
結論:この関数、じつはめちゃくちゃ気が利いてた
build_opener()
は、
- ちゃんとデフォルトの構成がある
- カスタムhandlerが来たらそっちを優先
- 被ってたらデフォルトは外す
- 最終的にベストな布陣でopenerを返す
という、「デフォルトも大事、でもあなたの個性も大切にする」みたいな、優しさと柔軟性にあふれた関数でした。
だけどまだよくわかってない
ぶっちゃけ、「handlerってなんなん?」とか「OpenerDirectorって何してるの?」みたいな疑問はまだ残ってます。
でも、そういうのを一つずつ調べていくのが、コードリーディングの醍醐味だよね。
沼にハマるのもまた一興。
おわりに
コードって、こうやって読んでるうちにだんだん全体像が見えてくるのが面白い。
理解しきれなくてもOK!読んだという事実が大事!

コメント