Modifying func_closure
Robert Brewer
fumanchu at amor.org
Thu Jul 8 16:21:30 EDT 2004
Jacek Generowicz:
> Python's closures are read-only. But is there a way of
> hacking one's
> way around this restriction?
Me:
> If I understand your requirements correctly, I would use
> new.function(), passing it the closure you want.
>
> return new.function(co, f.func_globals, newname, f.func_defaults,
> f.func_closure)
Jacek:
> But this doesn't help me to modify the binding in an original closure
> ... unless I'm missing your point.
Hmmm. perhaps I'm missing yours. It sounded to me like you had a
function with an associated closure, and wanted to modify that closure.
Given:
>>> def foo():
... a = 5
... def bar():
... return a
... return bar
...
>>> g = foo()
>>> g.func_closure
(<cell at 0x0116B310: int object at 0x002F9AF0>,)
>>> foo.func_code.co_cellvars
('a',)
Are you trying to modify g.func_closure or foo.func_code.co_cellvars or
both?
Either way, the only way I know to even fake that (in pure Python) is to
create a new function by copying the old one, substituting a different
func_closure or co_cellvars (see example below). Perhaps you can share
more about what you're trying to accomplish?
import new
def safe_tuple(seq):
"""Force func_code attributes to tuples of strings.
Many of the func_code attributes must take tuples, not lists,
and *cannot* accept unicode items--they must be coerced to strings
or the interpreter will crash.
"""
seq = map(str, seq)
return tuple(seq)
def new_closure(self, func, newname, cellvars=None, func_closure=None):
fcode = func.func_code
if cellvars is None:
cellvars = fcode.co_cellvars
if func_closure is None:
func_closure = func.func_closure
newco = new.code(fcode.co_argcount, fcode.co_nlocals,
fcode.co_stacksize,
# Notice co_consts should *not* be safe_tupled.
fcode.co_flags, codestr, tuple(fcode.co_consts),
safe_tuple(fcode.co_names),
safe_tuple(fcode.co_varnames),
fcode.co_filename, fcode.co_name,
fcode.co_firstlineno,
fcode.co_lnotab, safe_tuple(fcode.co_freevars),
safe_tuple(co_cellvars))
return new.function(newco, func.func_globals, newname,
func.func_defaults, func_closure)
[...this was hastily cut-and-pasted, so there may be some errors I
missed, but the principle is sound.]
If you're trying to hack the func_closure tuple itself, here's a handy
trick to dereference the values:
_derefblock = new.code(0, 0, 1, 3, '\x88\x00\x00Sd\x00\x00S', (None,),
('cell',), (), '', '', 2, '', ('cell',))
def deref_cell(cell):
return new.function(_derefblock, {}, "", (), (cell,))()
...pass this an item from a func_closure tuple, and you should get back
the value of the original outer variable.
Robert Brewer
MIS
Amor Ministries
fumanchu at amor.org
More information about the Python-list
mailing list