読者です 読者をやめる 読者になる 読者になる

pythonのクロージャ

python

半年以上前に書いた記事ですがネタもないので、 はやくはてなダイアリー市民になりたいので、せっかくなので公開してみます。
なお、その後にid:atsuoishimotoさんがfunc_closureのひみつ - atsuoishimotoの日記というもっと突っ込んだ内容の記事も書かれてますので、読んだことない人はぜひ読んでいただきたいです。

例えば以下のような、カリー化の初歩の初歩みたいな関数があるとして

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

# これをカリー化すると↓
def add_x(x):
    return lambda y: x + y
>>> curried_sum = add_x(10)
>>> curried_sum(20)
30

curried_sum = add_x(10)の10にアクセスしたい

クロージャの生成時にキャプチャされた「10」というオブジェクトにどうすればアクセスできるのか気になってしょうがなかったので調べてみました。細かいことが気になってしまうのが、僕の悪い癖。

>>> curried_sum
<function <lambda> at 0x00B1E0B0>
>>> dir(curried_sum)
['__call__', '__class__', '__closure__', '__code__', '__defaults__',
 '__delattr__', '__dict__', '__doc__', '__format__', '__get__',
 '__getattribute__', '__globals__', '__hash__', '__init__', '__module__',
 '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
 '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code',
 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

__closure__というものがあるのがわかります。ちなみにfunc_closureというのもありますが、これはもともとPython2.xではfunc_closureだったものが、Python3では__closure__に名前が変わるため、Python2.6と2.7にはこの2つが混在しているみたいです。

で、__closure__を見てみると

>>> curried_sum.__closure__
(<cell at 0x01227F90: int object at 0x009EF43C>,)

どうやらタプルに環境を保存しているようです。今度はこいつを調べてみます。

>>> sum_cell = curried_sum.__closure__[0]
>>> dir(sum_cell)
['__class__', '__cmp__', '__delattr__', '__doc__', '__format__',
 '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
 '__subclasshook__', 'cell_contents']

cell_contentsというものがありますね。

>>> sum_cell.cell_contents
10

というわけでアクセスできました。
おしまい。