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