Monday, November 19, 2012

back-porting non-local


There have been discussions about back-porting the nonlocal keyword from Python 3 to Python 2.
But, is there some crazy, misguided hack we can use to edit closures in Python 2?

Let's make a closure!

>>> def foo():
...    a = 2
...    def bar():
...       print a
...    return bar
...
>>> foo().func_closure
(<cell at 0x02515FF0: int object at 0x023AA6CC>,)

A closure apparently consists of a tuple of cell objects.  The tuple is immutable.  What about the cell itself?

>>> foo().func_closure[0].cell_contents
2
>>> foo().func_closure[0].cell_contents = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: attribute 'cell_contents' of 'cell' objects is not writable

Well played, Python 2.7 runtime.  Time to go nuclear: the python C api.

>>> b = foo()
>>> b()
2
>>> import ctypes
>>> ctypes.pythonapi.PyCell_Set(id(b.func_closure[0]), id(3))
0
>>> b()
3

So, there you have it.  The contents of a closure can be changed in Python 2.


1 comment: