Pythonのcontextlibが便利
Python2.5から使用可能なwith文を使用すると、try...except...finally パターンをカプセル化することができます。
(Python2.5の場合、次の一文をファイルの先頭に記述する必要があります。)
from __future__ import with_statement
このwith文を使うと、例えば、以下のように記述することで、openしたファイルをwith文から抜けた時点で自動的にcloseすることができます。
with open('hello.txt', 'r') as f: for line in f: print line # この時点でf.close()が実行されている
さて、このwith文に対応したクラスを自分で書く場合、そのクラスに__enter__と__exit__の二つのメソッドを定義します。
例えば、OpenGLでglBeginとglEndとの対応、glPushMatrixとglPopMatrixとの対応を自動的に保ってくれるライブラリを自作する場合、次のように記述します。
from OpenGL.GL import * class gl_begin(object): def __init__(self, mode): self.mode = mode def __enter__(self): glBegin(self.mode) def __exit__(self, exc_type, exc_value, traceback): glEnd() class gl_push_matrix(object): def __enter__(self): glPushMatrix() def __exit__(self, ext_type, ext_value, traceback): glPopMatrix()
使い方の例
with gl_push_matrix(): gluLookAt( 0,0,10, 0,0,0, 0,1,0 ) glColor3f(1,0,0) with gl_begin(GL_POLYGON): glVertex2f(0, -0.9) glVertex2f(-0.9, 0.9) glVertex2f(0.9, 0.9)
一方Rubyの場合、ブロック付きメソッドを使用することで、これを実現することができます。
require 'opengl' def with_gl_begin(mode) begin GL.Begin(mode) yield ensure GL.End end end def with_gl_push_matrix begin GL.PushMatrix yield ensure GL.PopMatrix end end
個人的には、Pythonの__enter__と__exit__を定義する方法は冗長かつ分かりにくく、この点でRubyのブロック付きメソッドは非常に優れていると感じていました。
しかし、Pythonのcontextlibというライブラリを使用すると、Rubyとほぼ同様の記述でこれが可能になることを最近知りました。
先程のPythonの例をcontextlibを使用すると、以下のようになります。
from contextlib import contextmanager @contextmanager def gl_begin(mode): try: glBegin(mode) yield finally: glEnd() @contextmanager def gl_push_matrix(): try: glPushMatrix() yield finally: glPopMatrix()
ファイルのopenの例ようにasで変数を受け取れるようにするには、yield式にオブジェクトを渡します。