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

getattr() on nested functions?

8 views
Skip to first unread message

Gabriel Rossetti

unread,
Aug 20, 2008, 4:34:38 AM8/20/08
to pytho...@python.org
Hello,

I can't get getattr() to return nested functions, I tried this :

>>> def toto():
... def titi():
... pass
... f = getattr(toto, "titi")
... print str(f)
...
>>> toto()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in toto
AttributeError: 'function' object has no attribute 'titi'
>>>

I thought that since functions are objects, that I could obtain it's
nested functions. How come it doesn't work and what can I do to
fix/replace it? I'm using it in code that is like this :

def __test(self, action, *args):
def request(params):
pass

def submit(params, values):
pass

def update(params, values):
pass

def delete(params):
pass

result = getattr(__test, action)(*args)

return resultToXml(result)

where "action" is a string containing either "request", "submit",
"update", or "delete". I was using an evel() with this form :

result = eval(action + "(params, values)")

but I didn't find that very clean.

Thank you,
Gabriel

Hrvoje Niksic

unread,
Aug 20, 2008, 5:05:04 AM8/20/08
to
Gabriel Rossetti <gabriel....@arimaz.com> writes:

> I can't get getattr() to return nested functions, I tried this :
>
>>>> def toto():
> ... def titi():
> ... pass
> ... f = getattr(toto, "titi")
> ... print str(f)
> ...
>>>> toto()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 4, in toto
> AttributeError: 'function' object has no attribute 'titi'
>>>>
>
> I thought that since functions are objects, that I could obtain it's
> nested functions.

Note that the nested function is created anew each time you call
toto(), so there's no reason for it to be stored anywhere. What is
stored is the internal compiled code object used to create the inner
function, but it's not easy to get to it, nor is it all that useful to
try. If you really need this, you can explicitly store the inner
function in the outer one's dict:

def outer():
if not hasattr(outer, 'inner'):
def inner():
pass
outer.inner = inner
f = outer.inner # same as getattr(outer, 'inner')
print f # same as print str(f)

>>> outer()
<function inner at 0xb7cfc294>
>>> outer.inner
<function inner at 0xb7cfc294>

> def __test(self, action, *args):
> def request(params):
> pass
> def submit(params, values):
> pass
> def update(params, values):
> pass
> def delete(params):
> pass
> result = getattr(__test, action)(*args)
> return resultToXml(result)
>
> where "action" is a string containing either "request", "submit",
> "update", or "delete".

Use locals()[action](*args).

Gabriel Genellina

unread,
Aug 20, 2008, 5:16:08 AM8/20/08
to pytho...@python.org
En Wed, 20 Aug 2008 05:34:38 -0300, Gabriel Rossetti
<gabriel....@arimaz.com> escribi�:

> I can't get getattr() to return nested functions, I tried this :
>
> >>> def toto():
> ... def titi():
> ... pass
> ... f = getattr(toto, "titi")
> ... print str(f)
> ...
> >>> toto()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 4, in toto
> AttributeError: 'function' object has no attribute 'titi'
> >>>
>
> I thought that since functions are objects, that I could obtain it's

> nested functions. How come it doesn't work and what can I do to
> fix/replace it? I'm using it in code that is like this :

Yes, functions are objects, but inner functions aren't attributes of the
outer; they live in its local namespace instead (and inner functions won't
exist until the outer function executes)

> def __test(self, action, *args):
> def request(params):
> pass
> def submit(params, values):
> pass
> def update(params, values):
> pass
> def delete(params):
> pass
> result = getattr(__test, action)(*args)
> return resultToXml(result)
>
> where "action" is a string containing either "request", "submit",

> "update", or "delete". I was using an evel() with this form :
>
> result = eval(action + "(params, values)")
>
> but I didn't find that very clean.

Try using locals()[action]

--
Gabriel Genellina

Bruno Desthuilliers

unread,
Aug 20, 2008, 5:31:59 AM8/20/08
to
Gabriel Rossetti a écrit :

> Hello,
>
> I can't get getattr() to return nested functions,

Of course. Nested functions are not attributes of their container function.

> I tried this :
>
> >>> def toto():
> ... def titi():
> ... pass
> ... f = getattr(toto, "titi")
> ... print str(f)
> ...
> >>> toto()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 4, in toto
> AttributeError: 'function' object has no attribute 'titi'
> >>>
>
> I thought that since functions are objects, that I could obtain it's
> nested functions.

Well, there's probably a very hackish way, but it's not worth the pain.
The fact that functions are objects doesn't make nested functions
methods of that object. If what you really want are methods, then you
can write your own callable:

class _Test(object):
def request(self, params):
pass
def submit(self, params, values):
pass
def update(self, params, values):
pass
def delete(self, params):
pass
def __call__(self, action, *args):
return resultToXml(getattr(self, action)(*args))

_test = _Test()

But read the remaining before...

> How come it doesn't work and what can I do to
> fix/replace it? I'm using it in code that is like this :
>
> def __test(self, action, *args):
> def request(params):
> pass
> def submit(params, values):
> pass
> def update(params, values):
> pass
> def delete(params):
> pass
> result = getattr(__test, action)(*args)
> return resultToXml(result)
>
> where "action" is a string containing either "request", "submit",
> "update", or "delete". I was using an evel() with this form :
>
> result = eval(action + "(params, values)")

Wrong use case for eval, as usual.

> but I didn't find that very clean.

indeed !-)

locals() is your friend.

def _test(self, action, *args):


def request(params):
pass
def submit(params, values):
pass
def update(params, values):
pass
def delete(params):
pass

result = locals()[action](*args)
return resultToXml(result)


HTH

Gabriel Rossetti

unread,
Aug 20, 2008, 11:01:29 AM8/20/08
to Gabriel Genellina, pytho...@python.org
Gabriel Genellina wrote:
> En Wed, 20 Aug 2008 05:34:38 -0300, Gabriel Rossetti
> <gabriel....@arimaz.com> escribi�:
>
>> I can't get getattr() to return nested functions, I tried this :

>>
>> >>> def toto():
>> ... def titi():
>> ... pass
>> ... f = getattr(toto, "titi")
>> ... print str(f)
>> ...
>> >>> toto()
>> Traceback (most recent call last):
>> File "<stdin>", line 1, in <module>
>> File "<stdin>", line 4, in toto
>> AttributeError: 'function' object has no attribute 'titi'
>> >>>
>>
>> I thought that since functions are objects, that I could obtain it's
>> nested functions. How come it doesn't work and what can I do to
>> fix/replace it? I'm using it in code that is like this :
>
> Yes, functions are objects, but inner functions aren't attributes of
> the outer; they live in its local namespace instead (and inner
> functions won't exist until the outer function executes)
Ok, yes, I see

>
>> def __test(self, action, *args):
>> def request(params):
>> pass
>> def submit(params, values):
>> pass
>> def update(params, values):
>> pass
>> def delete(params):
>> pass
>> result = getattr(__test, action)(*args)
>> return resultToXml(result)
>>
>> where "action" is a string containing either "request", "submit",
>> "update", or "delete". I was using an evel() with this form :
>>
>> result = eval(action + "(params, values)")
>>
>> but I didn't find that very clean.
>
> Try using locals()[action]
Yes, that works, thanks, I keep on forgetting that locals() exists!

Gabriel Rossetti

unread,
Aug 20, 2008, 11:03:10 AM8/20/08
to Bruno Desthuilliers, pytho...@python.org
Bruno Desthuilliers wrote:
> Gabriel Rossetti a écrit :
>> Hello,
>>
>> I can't get getattr() to return nested functions,
>
> Of course. Nested functions are not attributes of their container
> function.
Ok
Yes, very hackish :-)
Ok, thanks, that works :-), like I told Gabriel G., I keep on forgetting
locals() exists :-)
> HTH
> --
> http://mail.python.org/mailman/listinfo/python-list

Terry Reedy

unread,
Aug 20, 2008, 1:37:55 PM8/20/08
to pytho...@python.org

Gabriel Rossetti wrote:
> Bruno Desthuilliers wrote:
>> Gabriel Rossetti a écrit :

>>> I thought that since functions are objects, that I could obtain it's

>>> nested functions.
>>
>> Well, there's probably a very hackish way, but it's not worth the
>> pain.

What Bruno meant here, I believe, is that there is probably a hackish
way to get the nested functions, but it indeed would not be worth the pain.

>> The fact that functions are objects doesn't make nested
>> functions methods of that object.

A def statement is ultimately an assignment statement. The name of the
nested function is a local variable just like any other local name.

>> If what you really want are methods,
>> then you can write your own callable:
>>
>> class _Test(object):
>> def request(self, params):
>> pass
>> def submit(self, params, values):
>> pass
>> def update(self, params, values):
>> pass
>> def delete(self, params):
>> pass
>> def __call__(self, action, *args):
>> return resultToXml(getattr(self, action)(*args))
>>
>> _test = _Test()
>>

> Yes, very hackish :-)

No, not at all hackish, but one standard way to do what you want.

>> locals() is your friend.
>>
>> def _test(self, action, *args):
>> def request(params):
>> pass
>> def submit(params, values):
>> pass
>> def update(params, values):
>> pass
>> def delete(params):
>> pass
>> result = locals()[action](*args)
>> return resultToXml(result)
>>
>>

> Ok, thanks, that works :-), like I told Gabriel G., I keep on forgetting
> locals() exists :-)

Unlike the class approach, this requires recreating the constant
functions and dict with each call to _test. Quick to write but a bit
'dirty', in my opinion. Another standard idiom is to set up the
constants outside the function:

def request(params):
pass
def submit(params, values):
pass
def update(params, values):
pass
def delete(params):
pass

dispatch = {'request':request, 'submit':submit, 'update':update,
'delete':delete}

def _test(self, action, *args):
return resultToXmo(dispatch[action](*args))

Terry Jan Reedy


Gabriel Rossetti

unread,
Aug 21, 2008, 7:16:20 AM8/21/08
to Terry Reedy, pytho...@python.org
Terry Reedy wrote:
>
>
> Gabriel Rossetti wrote:
>> Bruno Desthuilliers wrote:
>>> Gabriel Rossetti a écrit :
>
>>>> I thought that since functions are objects, that I could obtain
>>>> it's nested functions.
>>>
>>> Well, there's probably a very hackish way, but it's not worth the pain.
>
> What Bruno meant here, I believe, is that there is probably a hackish
> way to get the nested functions, but it indeed would not be worth the
> pain.
Yes, I understood that :-)

>
> >> The fact that functions are objects doesn't make nested
>>> functions methods of that object.
>
> A def statement is ultimately an assignment statement. The name of
> the nested function is a local variable just like any other local name.
>
Ok

> >> If what you really want are methods,
>>> then you can write your own callable:
>>>
>>> class _Test(object):
>>> def request(self, params):
>>> pass
>>> def submit(self, params, values):
>>> pass
>>> def update(self, params, values):
>>> pass
>>> def delete(self, params):
>>> pass
>>> def __call__(self, action, *args):
>>> return resultToXml(getattr(self, action)(*args))
>>>
>>> _test = _Test()
>>>
>> Yes, very hackish :-)
>
> No, not at all hackish, but one standard way to do what you want.
>
>>> locals() is your friend.
>>>
>>> def _test(self, action, *args):
>>> def request(params):
>>> pass
>>> def submit(params, values):
>>> pass
>>> def update(params, values):
>>> pass
>>> def delete(params):
>>> pass
>>> result = locals()[action](*args)
>>> return resultToXml(result)
>>>
>>>
>> Ok, thanks, that works :-), like I told Gabriel G., I keep on
>> forgetting locals() exists :-)
>
> Unlike the class approach, this requires recreating the constant
> functions and dict with each call to _test. Quick to write but a bit
> 'dirty', in my opinion. Another standard idiom is to set up the
> constants outside the function:
>
> def request(params):
> pass
> def submit(params, values):
> pass
> def update(params, values):
> pass
> def delete(params):
> pass
> dispatch = {'request':request, 'submit':submit, 'update':update,
> 'delete':delete}
>
> def _test(self, action, *args):
> return resultToXmo(dispatch[action](*args))

That's how I had done it originally (before the use of eval()), but in
this case also, since the functions are still nested, they are
re-created and so is the dict, so I don' t see a gain from the locals()
method.
>
> Terry Jan Reedy
>
>
> --
> http://mail.python.org/mailman/listinfo/python-list

Bruno Desthuilliers

unread,
Aug 21, 2008, 8:03:31 AM8/21/08
to
Gabriel Rossetti a écrit :
> Terry Reedy wrote:
(snip)

>> Unlike the class approach, this requires recreating the constant
>> functions and dict with each call to _test. Quick to write but a bit
>> 'dirty', in my opinion. Another standard idiom is to set up the
>> constants outside the function:
>>
>> def request(params):
>> pass
>> def submit(params, values):
>> pass
>> def update(params, values):
>> pass
>> def delete(params):
>> pass
>> dispatch = {'request':request, 'submit':submit, 'update':update,
>> 'delete':delete}
>>
>> def _test(self, action, *args):
>> return resultToXmo(dispatch[action](*args))
>
> That's how I had done it originally (before the use of eval()), but in
> this case also, since the functions are still nested,

Uh ??? You probably want to re-read the above code snippet.

Gabriel Rossetti

unread,
Aug 21, 2008, 9:58:19 AM8/21/08
to Bruno Desthuilliers, pytho...@python.org
Uh...yes, I didn't see the external/parent function was no longer there.
I prefer to nest mine though because I have several parent functions for
different tasks, so each child/nested function has a diff.
implementation, I find that cleaner than having n*4+n top-level
functions (+ n dicts), e.g. I prefer this :

def __task1(self, action, *args):


def request(params):
pass
def submit(params, values):
pass
def update(params, values):
pass
def delete(params):
pass

return resultToXml(locals()[action](*args))

def __task2(self, action, *args):


def request(params):
pass
def submit(params, values):
pass
def update(params, values):
pass
def delete(params):
pass

return resultToXml(locals()[action](*args))


over this :

def task1_request(params):
pass
def task1_submit(params, values):
pass
def task1_update(params, values):
pass
def task1_delete(params):
pass

def task2_request(params):
pass
def task2_submit(params, values):
pass
def task2_update(params, values):
pass
def task2_delete(params):
pass

dispatch_task1 = {'request':task1_request, 'submit':task1_submit,
'update':task1_update, 'delete':task1_delete}
dispatch_task2 = {'request':task2_request, 'submit':task2_submit,
'update':task2_update, 'delete':task2_delete}

def _task1(self, action, *args):
return resultToXml(dispatch_task1[action](*args))

def _task2(self, action, *args):
return resultToXml(dispatch_task2[action](*args))


I could use your callable approach, but like you said it may not be
worth the trouble.

Gabriel

Bruno Desthuilliers

unread,
Aug 21, 2008, 11:14:12 AM8/21/08
to
Gabriel Rossetti a écrit :
(snip)

>
>
> I could use your callable approach, but like you said it may not be
> worth the trouble.

The "trouble" I was talking about was not with using callable objects,
but with trying to hack func.func_code to extract nested functions (if
ever possible).

But anyway... The point of using callable objects is to avoid going thru
the whole "setup" part again and again and again. Now if all your task
functions only differ by the dispatch dict, there's at least another
ways to avoid this runtime repetition - as well as coding repetition
FWIW (warning : untested code):

def _make_task(func):
dispatch = func()
def _task(action, *args):
return resultToXml(dispatch[action](*args))
_task.__name__ = _task.func_name = func.__name__
return _task

@_make_task
def _task1():


def request(params):
pass
def submit(params, values):
pass
def update(params, values):
pass
def delete(params):
pass

return locals()


HTH

castironpi

unread,
Aug 21, 2008, 1:50:11 PM8/21/08
to
On Aug 21, 10:14 am, Bruno Desthuilliers <bruno.

Here's more ideas:

Use a wrapper to give the function access to itself as an object:

@auto
def f( self, arg ):
assert self== f

In your original example:

@auto
def toto( toto ):
def titi():
pass


f = getattr(toto, "titi")

print str(f)

Should work, untested. Another is to use a wrapper to participate
functions in a dictionary:

@entry( 'a' )
def a( arg ):
a_stuff( )

@entry( 'b' )
def b( arg ):
b_stuff

You could even make 'entry' a class instance, so you can specialize
and vary it in other cases, untested.

Last one is just use a class, untested:

class __Test:
def __test(self, action, *args):
result = getattr(self, action)(*args)
return resultToXml(result)

def request(self,params):
pass

def submit(self,params, values):
pass

def update(self,params, values):
pass

def delete(self,params):
pass

Keep us posted which one works for you.

0 new messages