contextlibとか__import__とかfunctoolsとか

Temporary substitution of object in modules via with statement « Python recipes « ActiveState Code
Active StateのPython Recipesですごく勉強になるレシピを見つけた。
これは、with文で任意のモジュールの属性を一時的に書き換えてしまう例。

このレシピを読むと、

  1. contextlib, with文の利用例
  2. __import__ マジックメソッドの使い方
  3. functools.partialの使い方

が一度に学べる(学ぶきっかけが得られる?)。
これはとてもお得!

contextlib: with文を活用する

「コンテキストマネージャ」というものを作ってwith文で使える。
組み込みのコンテキストマネージャもいくつかあるみたい。
超有名なのはclosingかな♡

よくファイルとかデータベースのコネクションのcloseメソッドをtry-finallyブロックを使って呼ぶけど、それをwith文で書けるのがclosing。
http://www.python.jp/doc/nightly/library/contextlib.html#contextlib.closing

with closing(open("takumi.txt", "r")) as f:
    print f.read()

必ずcloseが呼ばれます☆
こんな風に、with文をうまく活用するための仕組みがcontextlibモジュールみたい。
Python Recipeの例では、モジュールの差し替えをやらせている。

__import__マジックメソッド

http://d.hatena.ne.jp/perezvon/20080129/1201623922
ここでは__import__のことが詳しく考察されているけど(ちょっと僕のレベルだと意味が解らない..)、

  1. __import__を使うとモジュールの名前を文字列で渡してインポートできる。
  2. インポートは最初の1回やったら再度ロードし直さない。
  3. でもreload関数を使うと何度もインポートし直せる。

ってことは判った。
それで、Python Recipeでは

def _findattr(mod, rest):
    parent, dot, rest = rest.partition('.')
    if rest:
        return _findattr(getattr(mod, parent), rest)
    else:
        return mod, parent

def find_in_module(fullname):
    modname, dot, rest = fullname.partition('.')
    module, objname = _findattr(__import__(modname), rest)
    return getattr(module, objname), partial(setattr, module, objname)

こういう風に、モジュールをインポートして望みの属性に辿りつくまでgetattrを繰り返すんだね。

functools.partial: 引数の部分適用をする

pythonの関数は第1級オブジェクトなので、関数を引数に取ったり関数を返す関数を作ったりできる。
そして、関数の引数を部分適用した関数を作ることもできる。

例えば、

def foo(x, y):
    return x + y

import functools
bar = functools.partial(foo, 1)
print bar(8) #=> 9

こんな感じかな。
このコードのbarみたいに、fooの第1引数を1で固定した別の関数を作るっていうのが部分適用。

Python Recipeでは

partial(setattr, module, objname)

こんなことをしている。
最初見たときすぐには意味が解らなかったんだけど、settattr関数は「オブジェクト・属性名・属性値」の順に引数を取って、対象のオブジェクトの属性に代入を行うので、「オブジェクト・属性名」まで束縛した新しい別の関数を作っているってことなんだね。
そして後ろの行でpartialで作った関数に差し替えたい値を渡している。

それと、文字列にpartitionっていうメソッドがあることも初めて知った。
色々ためになるな〜と思った。