Search on the blog

2015年11月9日月曜日

コンテキスト用ユーティリティ

 Pythonのcontextlibというモジュールが興味深かったので、メモ。

語句の意味
「コンテキスト」という語句はプログラミングを書いてるとよく聞くが、理解が曖昧だったので調べてみた。
context managerは、リソースの割り当て、解放を必要なときに行ってくれるもの。
contextは、”状態”と考えるといい。リソースが開いている、トランザクションが実行中などの”状態”を指す。

サンプルソース
まず、contextlibを使う前に以下のソースを考える。
if __name__ == '__main__':
    with open('test.in') as f:
        for line in f:
            print line
with 句を抜けると、ファイルがcloseされるのは知っているけど、どうなってるのか?

以下のように、__enter__と__exit__を実装したクラスを作ると、with句の前後でそれぞれのメソッドが呼ばれる。これをうまく使えば、fileのopen、closeが出来そうなのが分かる。
class HogeContext:
    def __enter__(self):
        print "open context..."
    def __exit__(self, exc_type, exc_value, traceback):
        print "close context..."

if __name__ == '__main__':
    with HogeContext():
        print "hello, world"

それじゃあ、__enter__と__exit__を実装していないクラスだとwith句は使えないのか?
と思うが、それを解決するのがcontextlib。
from contextlib import closing

class Foo:
 def doit(self):
  print "do something..."
 def error(self):
  raise Exception('Foo exception...')
 def close(self):
  print "closing..."
 
if __name__ == '__main__':
 with closing(Foo()) as foo:
  foo.doit()
  foo.error()
上のようにclosing(obj)はwith句を抜けたときにobj.close()が呼ばれるようなコンテキストマネージャを返す。
でこれ何が嬉しいの?というと、
from contextlib import closing
import urllib

with closing(urllib.urlopen('http://www.python.org')) as page:
    for line in page:
        print line
のように使えて、嬉しい。

より柔軟なコンテキストの管理をしたい場合は、以下のようにデコレータを使ってコンテキストマネージャを作ることもできる。
from contextlib import contextmanager

@contextmanager
def SomeContext():
    print "begin some context..."
    try:
        yield
    finally:
        print "end some context..."

if __name__ == '__main__':
    with SomeContext():
        print "Hello, world."    
        raise Exception()

0 件のコメント:

コメントを投稿