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式にオブジェクトを渡します。