Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a function refer stably to itself?

[The code in the original version was badly messed up. Even after I fixed the code, several highly confusing typos remained in the post. I believe I finally fixed all of them too. Profuse apologies.]

The two calls to alias below produce different outputs, because the object associated with the variable my_own_id changes between the two calls:

>>> def my_own_id():
...     me = my_own_id
...     return id(me)
... 
>>> alias = my_own_id
>>> alias()
4301701560
>>> my_own_id = None
>>> alias()
4296513024

What can I assign to me in the definition of my_own_id so that its output remains invariant wrt subsequent re-definitions of the my_own_id variable? (IOW, so that the internal me variable always refers to the same function object?)

(I can get the current frame (with inspect.currentframe()), but it contains only a reference to the current code object, not to the current function.)

P.S. The motivation for this question is only to know Python better.

like image 764
kjo Avatar asked Aug 10 '12 19:08

kjo


2 Answers

It seems that referring to my_own_id will look for 'my_own_id' in the global namespace dictionary, so it will always be the name used on function definition. Since that name can be assigned to different values, the value retrieved can also change. If you make me a default argument, you can assign it to the function itself at function definition to keep a reference to the actual function.

You could use this decorator which implicitly passes the original function itself as the first argument.

>>> from functools import wraps
>>> def save_id(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(func, *args, **kwargs)
        return wrapper


>>> @save_id
def my_own_id(me): # me is passed implicitly by save_id
    return id(me)

>>> alias = my_own_id
>>> alias()
40775152
>>> my_own_id = 'foo'
>>> alias()
40775152
like image 159
jamylak Avatar answered Oct 22 '22 16:10

jamylak


Indeed, if you rely only on the function name, if that name is overitten in the global variable space (in the module the function was defined), a reference using the name of the function itslef will fail

The easier, more maintanable way is to write a decorator for that, that would provide a nonlocalvariable containing a reference to the function itself.

from functools import wraps

def know_thyself(func):
   @wraps(func):
   def new_func(*args, **kwargs):
        my_own_id = func
        return func(*args, **kwargs)
   return new_func

And can be used as:

>>> @know_thyself
... def my_own_id():
...     me = my_own_id
...     return id(me)
... 

There is another possible approach, far from being this clean, using frame introspection and rebuilding a new function re-using the same object code. I had used this on this post about a self-referential lambda expression in Python: http://metapython.blogspot.com.br/2010/11/recursive-lambda-functions.html

like image 34
jsbueno Avatar answered Oct 22 '22 18:10

jsbueno