From: Steven D'Aprano on
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



--
Steven
From: Arnaud Delobelle on
Steven D'Aprano <steve(a)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
From: Arnaud Delobelle on
MRAB <python(a)mrabarnett.plus.com> writes:


> Does this mean that Python needs, say, __function__ (and perhaps also
> __module__)?

See PEP 3130 "Access to Current Module/Class/Function" (rejected)
(http://www.python.org/dev/peps/pep-3130/)

--
Arnaud
From: Gabriel Genellina on
En Wed, 10 Feb 2010 10:59:41 -0300, Muhammad Alkarouri
<malkarouri(a)gmail.com> escribi�:

> What is the simplest way to access the attributes of a function from
> inside it, other than using its explicit name?
> In a function like f below:
>
> def f(*args):
> f.args = args
> print args
>
> is there any other way?

See this:

>>> def foo(): pass
....
>>> import sys
>>> sys.getrefcount(foo)
2

The 2 means that there is a *single* reference to the function - the foo
name in the global namespace. No other reference exists, there is no
hidden attribute somewhere that you may use. If you want another way to
reach the function, you'll have to add it yourself.

I've written a decorator for "injecting" a __function__ name into the
function namespace, but I can't find it anywhere. I think I implemented
it by adding a fake additional argument and replacing LOAD_GLOBAL with
LOAD_NAME in the bytecode.

> I am guessing the next question will be: should I really care? It just
> feels like there should be a way, but I am not able to verbalise a
> valid one at the moment, sorry.

One reason would be to write truly recursive functions (currently, a
recursive call involves a name lookup, which could potentially return a
different function). Another one, to implement some kind of tail call
optimization.

--
Gabriel Genellina

From: Terry Reedy on
On 2/10/2010 4:49 PM, Gabriel Genellina wrote:

> I've written a decorator for "injecting" a __function__ name into the
> function namespace, but I can't find it anywhere. I think I implemented
> it by adding a fake additional argument and replacing LOAD_GLOBAL with
> LOAD_NAME in the bytecode.

The decorator only needs to replace the defaults args tuple.
It does not even need to know the parameter name,
just that it is the only (or last) with a default .

def f(n, me=None):
if n > 0: return n*me(n-1)
elif n==0: return 1

f.__defaults__ = (f,) # 3.1
print(f(5))
# 120

To generalize:

def g(a,b=1,me=None):
if a: return me(0)
else: return 41+b

g.__defaults__ = g.__defaults__[:len(g.__defaults__)-1] + (g,)
print(g(3))
#42

Of course, user could still screw up recursion by providing another
value for 'me'. This strikes me as about a likely (low) as a user
screwing up recursion in a library module function by rebinding the name
in the imported module.

Terry Jan Reedy