Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Function attributes

1 view
Skip to first unread message

Muhammad Alkarouri

unread,
Feb 10, 2010, 8:59:41 AM2/10/10
to
Hi everyone,

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?
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.

Regards,

Muhammad Alkarouri

Steven D'Aprano

unread,
Feb 10, 2010, 9:36:32 AM2/10/10
to
On Wed, 10 Feb 2010 05:59:41 -0800, Muhammad Alkarouri wrote:

> Hi everyone,
>
> 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?

Not built-in.


> 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.

I completely agree with you. It is a wart that functions are only able to
refer to themselves by name, because if the name changes, things break.
Consider:

old_f = f # save the old version of the function

def f(x):
return old_f(x+1) # make a new function and call it f

This won't work correctly, because old_f still tries to refer to itself
under the name "f", and things break very quickly.

--
Steven

Krister Svanlund

unread,
Feb 10, 2010, 9:46:00 AM2/10/10
to pytho...@python.org
> --
> http://mail.python.org/mailman/listinfo/python-list
>

This sounds like something you shouldn't be doing. You should probably
use a class instead.

(Sending again to get it on the list >_<)

John Posner

unread,
Feb 10, 2010, 10:08:47 AM2/10/10
to Steven D'Aprano

They didn't break immediately for me -- what am I missing?:

#-------------------
def f(): return "old func"
g = f
print "name attr of f:", f.__name__
print "name attr of g:", g.__name__

def f(): return "new func"
print "name attr of f:", f.__name__
print "name attr of g:", g.__name__

print "*** calling function currently named f:"
print f()
print "*** calling function currently named g:"
print g()
#-------------------

program output (Python 2.6.4):

name attr of f: f
name attr of g: f
name attr of f: f
name attr of g: f
*** calling function currently named f:
new func
*** calling function currently named g:
old func

-John

Bruno Desthuilliers

unread,
Feb 10, 2010, 10:24:56 AM2/10/10
to
John Posner a écrit :

> On 2/10/2010 9:36 AM, Steven D'Aprano wrote:
>> On Wed, 10 Feb 2010 05:59:41 -0800, Muhammad Alkarouri wrote:
>>
(snip)

>>> def f(*args):
>>> f.args = args
>>> print args
>>>

(snip)


>> I completely agree with you. It is a wart that functions are only able to
>> refer to themselves by name, because if the name changes, things break.
>> Consider:
>>
>> old_f = f # save the old version of the function
>>
>> def f(x):
>> return old_f(x+1) # make a new function and call it f
>>
>> This won't work correctly, because old_f still tries to refer to itself
>> under the name "f", and things break very quickly.
>
> They didn't break immediately for me -- what am I missing?:

The fact that in the OP's snippet, code inside f's body refers to f by
its name.

Steven D'Aprano

unread,
Feb 10, 2010, 10:33:34 AM2/10/10
to
On Wed, 10 Feb 2010 10:08:47 -0500, John Posner wrote:

>> This won't work correctly, because old_f still tries to refer to itself
>> under the name "f", and things break very quickly.
>
> They didn't break immediately for me -- what am I missing?:

The original function f doesn't try to refer to itself in any way. With
no recursive call or access, it doesn't matter what f is named.

See this example instead:


>>> def f(x):
... if x < 0:
... print "original"
... return f(-x)
... else:
... return x+1
...
>>> f(2)
3
>>> f(-2)
original
3
>>>
>>> old_f = f
>>> def f(x):
... if x > 0:
... return old_f(-x)
... else:
... return x
...
>>> f(-1)
-1
>>> f(1)
original
original
original
original
original
original
[...]
File "<stdin>", line 3, in f
File "<stdin>", line 4, in f
File "<stdin>", line 3, in f
RuntimeError: maximum recursion depth exceeded

--
Steven

MRAB

unread,
Feb 10, 2010, 12:23:36 PM2/10/10
to Python-list
Stephen Hansen wrote:
> On Wed, Feb 10, 2010 at 6:36 AM, Steven D'Aprano
> <st...@remove-this-cybersource.com.au
> <mailto:st...@remove-this-cybersource.com.au>> wrote:
>
> On Wed, 10 Feb 2010 05:59:41 -0800, Muhammad Alkarouri wrote:
> > 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
>
> Not built-in.
>
> > 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.
>
> I completely agree with you. It is a wart that functions are only
> able to
> refer to themselves by name, because if the name changes, things break.
>
>
> I agree its slightly... in-elegant, or sub-optimal, but I'm not sure I
> want to call it a *wart*. If one calls it a wart, there might be
> inspiration to fix it.
>
> And this sounds like a precursor to making "self" non-explicit, and I
> *really* like my explicit self. :)
>
> Then again, I have been slightly bruised by this sub-optimal situation
> before.
>
> I tried to do a function using a frame hack to get the information
> easily, but failed. My long years of avoiding frame hacks like the
> plague have left me deficient. :(
>
Does this mean that Python needs, say, __function__ (and perhaps also
__module__)?

John Posner

unread,
Feb 10, 2010, 1:15:27 PM2/10/10
to
On 2/10/2010 10:24 AM, Bruno Desthuilliers wrote:
>>
>> They didn't break immediately for me -- what am I missing?:
>
> The fact that in the OP's snippet, code inside f's body refers to f by
> its name.

Of course! Tx. -John

Arnaud Delobelle

unread,
Feb 10, 2010, 1:31:23 PM2/10/10
to

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

>>> @bindfunction
... def factorial(this_function, n):
... if n > 0:
... return n * this_function(n - 1)
... else:
... return 1
...
>>> factorial(15)
1307674368000L
>>> fac = factorial
>>> fac(15)
1307674368000L
>>> factorial = 'foobar'
>>> fac(15)
1307674368000L

--
Arnaud

Steven D'Aprano

unread,
Feb 10, 2010, 1:58:02 PM2/10/10
to
On Wed, 10 Feb 2010 17:23:36 +0000, MRAB wrote:

> Stephen Hansen wrote:
>> On Wed, Feb 10, 2010 at 6:36 AM, Steven D'Aprano
>> <st...@remove-this-cybersource.com.au
>> <mailto:st...@remove-this-cybersource.com.au>> wrote:
>>
>> On Wed, 10 Feb 2010 05:59:41 -0800, Muhammad Alkarouri wrote:
>> > 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
>>
>> Not built-in.

[...]

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

Python already has __module__, except that it is spelled:

import __main__


As for __function__, perhaps, but a solution I would be satisfied with
would be to make it optional. Most functions are not recursive, and do
not attach attributes to themselves. For the few that do need to refer to
themselves, it would be sufficient to call a decorator to get the
behaviour needed. I thought I had come up with such a thing, once before,
but I can't find it now :(


--
Steven

Steven D'Aprano

unread,
Feb 10, 2010, 2:04:58 PM2/10/10
to
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

Arnaud Delobelle

unread,
Feb 10, 2010, 4:39:33 PM2/10/10
to
Steven D'Aprano <st...@REMOVE-THIS-cybersource.com.au> writes:

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

Arnaud Delobelle

unread,
Feb 10, 2010, 4:47:19 PM2/10/10
to
MRAB <pyt...@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

Gabriel Genellina

unread,
Feb 10, 2010, 4:49:40 PM2/10/10
to pytho...@python.org
En Wed, 10 Feb 2010 10:59:41 -0300, Muhammad Alkarouri
<malka...@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

Terry Reedy

unread,
Feb 10, 2010, 10:25:00 PM2/10/10
to pytho...@python.org
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


Gabriel Genellina

unread,
Feb 11, 2010, 6:36:40 PM2/11/10
to pytho...@python.org
En Thu, 11 Feb 2010 00:25:00 -0300, Terry Reedy <tjr...@udel.edu>
escribi�:

> 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))

This is simple to implement, but requires changing the function
definition. My goal was to keep the original code unchanged, that is,
leave it as:

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

(like a normal, recursive function), and make the 'f' inside the function
body "magically" refer to the function itself.

> 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.

Well, if people really want to shoot themselves in the foot, there's
nothing we can do to avoid that...

--
Gabriel Genellina

Terry Reedy

unread,
Feb 12, 2010, 12:31:14 AM2/12/10
to pytho...@python.org
On 2/11/2010 6:36 PM, Gabriel Genellina wrote:

> This is simple to implement, but requires changing the function
> definition. My goal was to keep the original code unchanged, that is,
> leave it as:
>
> def f(n):
> if n > 0: return n*f(n-1)
> elif n==0: return 1
>
> (like a normal, recursive function), and make the 'f' inside the function
> body "magically" refer to the function itself.

Someone did this several years ago with a bytecode hack. I believe it
was in the cookbook. It was rejected as part of standard CPython.

Arnaud Delobelle

unread,
Feb 12, 2010, 2:29:12 AM2/12/10
to
"Gabriel Genellina" <gags...@yahoo.com.ar> writes:

> En Thu, 11 Feb 2010 00:25:00 -0300, Terry Reedy <tjr...@udel.edu>

> escribió:


>> 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))
>
> This is simple to implement, but requires changing the function
> definition. My goal was to keep the original code unchanged, that is,
> leave it as:
>
> def f(n):
> if n > 0: return n*f(n-1)
> elif n==0: return 1
>
> (like a normal, recursive function), and make the 'f' inside the function
> body "magically" refer to the function itself.

I posted an example of a decorator that does just this in this thread a
couple of days ago:

http://mail.python.org/pipermail/python-list/2010-February/1235742.html

It doesn't require any bytecode hacking, although it requires breaking
apart the function object and making a new one from the bits.

--
Arnaud

Gabriel Genellina

unread,
Feb 12, 2010, 8:48:30 PM2/12/10
to pytho...@python.org
En Fri, 12 Feb 2010 04:29:12 -0300, Arnaud Delobelle
<arn...@googlemail.com> escribi�:

> I posted an example of a decorator that does just this in this thread a
> couple of days ago:
>
> http://mail.python.org/pipermail/python-list/2010-February/1235742.html

Ouch! I didn't see your post, nor several other earlier posts in this
thread. In fact, I thought mine was the *first* reply when I wrote it!

Yes, of course, your code does exactly that without any bytecode hacks.

--
Gabriel Genellina

Steve Howell

unread,
Feb 13, 2010, 4:53:09 AM2/13/10
to

You should care. :)

You can avoid referring to f twice by using an inner method with a
name that is less subject to change or influence from outside forces:

def countdown_function_that_might_get_renamed(
n,
preamble,
postamble):
def recurse(n):
print preamble, n, postamble
if n:
recurse(n-1)
recurse(n)

countdown_function_that_might_get_renamed(10, "before", "after")

Note that the example makes the recursive call more concise, by
avoiding the need to resend the parameters that don't change during
the recursion ("preamble" and "postamble"). Of course, you do trade
off some terseness with the inner method, but it's only two lines of
code and one level of indentation.

For a somewhat related discussion see this short thread:

http://groups.google.com/group/comp.lang.python/browse_thread/thread/197221f82f7c247b

In particular see Gabriel's response to me. The fact that you are
saving off f.args makes me think that you are solving a problem
similar to mine, but I could be wrong.

Mark Lawrence

unread,
Feb 13, 2010, 6:02:24 AM2/13/10
to pytho...@python.org
Gabriel Genellina wrote:
> En Fri, 12 Feb 2010 04:29:12 -0300, Arnaud Delobelle
> <arn...@googlemail.com> escribi�:
>
>> I posted an example of a decorator that does just this in this thread a
>> couple of days ago:
>>
>> http://mail.python.org/pipermail/python-list/2010-February/1235742.html
>
> Ouch! I didn't see your post, nor several other earlier posts in this
> thread. In fact, I thought mine was the *first* reply when I wrote it!
>

It's not just me then. I'm using Thunderbird on Windows Vista to view
the group via gmane, and notice the earlier parts of a thread can show
up hours or even days after the later ones. Would something who
understands these things be kind enough to explain why this happens, or
at least provide the keywords needed for google, as I haven't got a clue
where to start.

Steve Holden

unread,
Feb 13, 2010, 8:14:29 AM2/13/10
to pytho...@python.org
Mark Lawrence wrote:
> Gabriel Genellina wrote:
>> En Fri, 12 Feb 2010 04:29:12 -0300, Arnaud Delobelle
>> <arn...@googlemail.com> escribi�:
>>
>>> I posted an example of a decorator that does just this in this thread a
>>> couple of days ago:
>>>
>>> http://mail.python.org/pipermail/python-list/2010-February/1235742.html
>>
>> Ouch! I didn't see your post, nor several other earlier posts in this
>> thread. In fact, I thought mine was the *first* reply when I wrote it!
>
> It's not just me then. I'm using Thunderbird on Windows Vista to view
> the group via gmane, and notice the earlier parts of a thread can show
> up hours or even days after the later ones. Would something who
> understands these things be kind enough to explain why this happens, or
> at least provide the keywords needed for google, as I haven't got a clue
> where to start.
>
There are two main channels feeding the comp.lang.python newsgroup.
Gmane is primarily a news (NNTP-based) service, though for reasons best
know to the organizers they choose to rename the standard newsgroups to
fit their own idea of how the universe should be organized, so there the
group becomes gmane.comp.python.general.

The first feed is the general interchange of articles between NNTP
servers: while this is usually a reliable interchange mechanism, an
individual server can "glitch" occasionally (by losing network
connectivity, for example, or when some part of its news support
mechanism breaks temporarily).

The second is the python-list mailing list, maintained as a part of the
python.org infrastructure (by a hard-working team of volunteers, I
should add, who get little enough credit for the sterling work they do
day in day out to keep the real information flowing and the spam out).

More recently the waters have been muddied by Google Groups, which sadly
seems at time to be so badly managed (or simply not managed?) as to make
it a principal source of spam. This surprises me, as Gmail appears to
have very effective spam filtering. Many people appear to use Google
Groups as their primary interface to newsgroups and/or mailing lists to
the extent that they believe the Groups interface to be what everyone
uses to access the list.

The task of maintaining a two-way bridge between the comp.lang.python
NNTP community and the python-list email community while filtering out
the spam and ensuring that posts aren't duplicated too often is a
difficult one. For example, I hit "reply all" to your message, and had I
not trimmed the headers my NNTP client (Thunderbird) would have sent
copies to you, to the mailing list AND to the newsgroup. Sometimes such
messages do appear twice, particularly (in my experience) to recipients
who use the email channel.

In short, these things happen because life is complicated and the
technical solutions available to disambiguate the feeds aren't perfect.
So you might consider filing the glitches under "shit happens". Speaking
personally I wouldn't have a clue how to keep this stuff going on a
daily basis at the volume level we see on this group, and I am very
grateful to everyone who does.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon is coming! Atlanta, Feb 2010 http://us.pycon.org/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/

Mark Lawrence

unread,
Feb 13, 2010, 8:23:33 AM2/13/10
to pytho...@python.org
Steve Holden wrote:
> Mark Lawrence wrote:
>> Gabriel Genellina wrote:
>>> En Fri, 12 Feb 2010 04:29:12 -0300, Arnaud Delobelle
>>> <arn...@googlemail.com> escribi�:
>>>
>>>> I posted an example of a decorator that does just this in this thread a
>>>> couple of days ago:
>>>>
>>>> http://mail.python.org/pipermail/python-list/2010-February/1235742.html

Many thanks for the explanation.

Kindest regards.

Mark Lawrence

Terry Reedy

unread,
Feb 14, 2010, 4:23:30 PM2/14/10
to pytho...@python.org
On 2/13/2010 8:14 AM, Steve Holden wrote:

> Gmane is primarily a news (NNTP-based) service, though for reasons best
> know to the organizers they choose to rename the standard newsgroups to
> fit their own idea of how the universe should be organized, so there the
> group becomes gmane.comp.python.general.

As I understand the site, the last I read a year or two ago, gmane is a
technical mailinglist-to-news gateway. It does not mirror newsgroups,
standard or not. It only mirrors lists such as python-list, not
newsgroups, such comp.lang.python. In doing so, it organizes the lists
into categories like g.comp.python, which has about 200 subgroups
mirroring 200 python lists.

Steve Holden

unread,
Feb 14, 2010, 4:32:36 PM2/14/10
to pytho...@python.org
That would make more sense. I know that the python.org infrastructure
acts as a gateway between the mailing list and the newsgroup.
0 new messages