Self function
Arnaud Delobelle
arnodel at googlemail.com
Thu May 7 12:55:11 EDT 2009
Luis Alberto Zarrabeitia Gomez <kyrie at uh.cu> writes:
> A bit offtopic: a while ago I think I saw a recipe for a decorator
> that, via bytecode hacks, would bind otherwise global names to the
> local namespace of the function. Can anyone remember it/point me to
> it? An @bind decorator that would 'localize' all the global names,
> including the still unexistent but already know function name, would
> guarantee that at least recursion calls the same function instead of
> "whatever happens to be bound to their name at runtime". If it wasn't
> a hack, anyway.
I remember a discussion on python-ideas, and I wrote this decorator at
the time:
def new_closure(vals):
args = ','.join('x%i' % i for i in range(len(vals)))
f = eval("lambda %s:lambda:(%s)" % (args, args))
return f(*vals).func_closure
def localize(f):
f_globals = dict((n, f.func_globals[n]) for n in f.func_code.co_names)
f_closure = ( f.func_closure and
new_closure([c.cell_contents for c in f.func_closure]) )
return type(f)(f.func_code, f_globals, f.func_name,
f.func_defaults, f_closure)
Unfortunately it fails for recursive functions:
>>> @localize
... def fac(n):
... return 1 if n <= 1 else n*fac(n - 1)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "localize.py", line 7, in localize
f_globals = dict((n, f.func_globals[n]) for n in f.func_code.co_names)
File "localize.py", line 7, in <genexpr>
f_globals = dict((n, f.func_globals[n]) for n in f.func_code.co_names)
KeyError: 'fac'
It can be fixed, but not very easily precisely because the function is
recursive. I've hacked a quick way to do it below.
def reclocalize(f):
def wrapper(*args, **kwargs):
return recf(*args, **kwargs)
def getglobal(name):
return wrapper if name == f.__name__ else f.func_globals[name]
f_globals = dict((n, getglobal(n)) for n in f.func_code.co_names)
f_closure = ( f.func_closure and
new_closure([c.cell_contents for c in f.func_closure]) )
recf = type(f)(f.func_code, f_globals, f.func_name,
f.func_defaults, f_closure)
return wrapper
Now it works as intended:
>>> @reclocalize
... def fac(n):
... return 1 if n <= 1 else n*fac(n - 1)
...
>>> fac(10)
3628800
>>> foo = fac
>>> del fac
>>> foo(10)
3628800
--
Arnaud
More information about the Python-list
mailing list