Function attributes

Arnaud Delobelle arnodel at googlemail.com
Wed Feb 10 16:39:33 EST 2010


Steven D'Aprano <steve at REMOVE-THIS-cybersource.com.au> writes:

> On Wed, 10 Feb 2010 18:31:23 +0000, Arnaud Delobelle wrote:
>
>> It's not ideal, but you can use a decorator like this to solve this
>> problem:
>> 
>> def bindfunction(f):
>>     def bound_f(*args, **kwargs):
>>         return f(bound_f, *args, **kwargs)
>>     bound_f.__name__ = f.__name__
>>     return bound_f
>
> Ah, very nice. Perhaps it's better to use functools.wraps?
>
> import functools
>
> def bindfunction(f):
>     @functools.wraps(f)
>     def bound_f(*args, **kwargs):
>         return f(bound_f, *args, **kwargs)
>     return bound_f

I think I wrote this before functools :).  Anyway it still doesn't work
with mutually recursive functions.  I also tried another approach (can't
find the file anymore, so I've redone it, sorry no time to make it very
readable) as below.  It doesn't require an extra first argument for the
function and it takes care of mutually recursive functions if used as in
the example.


def bindglobals(*args):
    """
    Binds all the globals in all the arguments which must be functions.
    return the new bound functions.  When called with a single argument,
    return the bound function so it can be used as a decorator.  It is
    assumed that all argument are functions and have the same global
    namespace
    """
    function_names = [f.__name__ for f in args]
    def get_global(f, n):
        d = f.func_globals
        if n not in d:
            d = d['__builtins__'].__dict__
        return d[n]
    bound_globals = dict(
        (n, get_global(f, n))
        for f in args for n in f.func_code.co_names
        if n not in function_names
        )
    bound_functions = [
        type(f)(f.func_code, bound_globals, f.func_name,
                f.func_defaults, f.func_closure)
        for f in args
        ]
    bound_globals.update(zip(function_names, bound_functions))
    if len(args) == 1:
        return bound_functions[0]
    else:
        return bound_functions

# Example

@bindglobals
def fac(n):
    return 1 if n <= 1 else n*fac(n - 1)

# Decorator syntax cannot be used with mutually recursive functions:

def even(n):
    return True if not n else odd(n - 1)

def odd(n):
    return False if not n else even(n - 1)

even, odd = bindglobals(even, odd)

# Example in action:

>>> fac(10)
3628800
>>> f = fac
>>> fac = 'foo'
>>> f(10)
3628800
>>> even(5), odd(5)
(False, True)
>>> e, o = even, odd
>>> even, odd = 'spam', 'eggs'
>>> e(5), o(5)
(False, True)

This is proof of concept stuff - probably very fragile!

-- 
Arnaud



More information about the Python-list mailing list