How to get current function into a variable?
Solution 1
The stack frame tells us what code object we're in. If we can find a function object that refers to that code object in its __code__
attribute, we have found the function.
Fortunately, we can ask the garbage collector which objects hold a reference to our code object, and sift through those, rather than having to traverse every active object in the Python world. There are typically only a handful of references to a code object.
Now, functions can share code objects, and do in the case where you return a function from a function, i.e. a closure. When there's more than one function using a given code object, we can't tell which function it is, so we return None
.
import inspect, gc
def giveupthefunc():
frame = inspect.currentframe(1)
code = frame.f_code
globs = frame.f_globals
functype = type(lambda: 0)
funcs = []
for func in gc.get_referrers(code):
if type(func) is functype:
if getattr(func, "__code__", None) is code:
if getattr(func, "__globals__", None) is globs:
funcs.append(func)
if len(funcs) > 1:
return None
return funcs[0] if funcs else None
Some test cases:
def foo():
return giveupthefunc()
zed = lambda: giveupthefunc()
bar, foo = foo, None
print bar()
print zed()
I'm not sure about the performance characteristics of this, but i think it should be fine for your use case.
Solution 2
I recently spent a lot of time trying to do something like this and ended up walking away from it. There's a lot of corner cases.
If you just want the lowest level of the call stack, you can just reference the name that is used in the def
statement. This will be bound to the function that you want through lexical closure.
For example:
def recursive(*args, **kwargs):
me = recursive
me
will now refer to the function in question regardless of the scope that the function is called from so long as it is not redefined in the scope where the definition occurs. Is there some reason why this won't work?
To get a function that is executing higher up the call stack, I couldn't think of anything that can be reliably done.
Solution 3
This is what you asked for, as close as I can come. Tested in python versions 2.4, 2.6, 3.0.
#!/usr/bin/python
def getfunc():
from inspect import currentframe, getframeinfo
caller = currentframe().f_back
func_name = getframeinfo(caller)[2]
caller = caller.f_back
from pprint import pprint
func = caller.f_locals.get(
func_name, caller.f_globals.get(
func_name
)
)
return func
def main():
def inner1():
def inner2():
print("Current function is %s" % getfunc())
print("Current function is %s" % getfunc())
inner2()
print("Current function is %s" % getfunc())
inner1()
#entry point: parse arguments and call main()
if __name__ == "__main__":
main()
Output:
Current function is <function main at 0x2aec09fe2ed8>
Current function is <function inner1 at 0x2aec09fe2f50>
Current function is <function inner2 at 0x2aec0a0635f0>
Solution 4
Here's another possibility: a decorator that implicitly passes a reference to the called function as the first argument (similar to self
in bound instance methods). You have to decorate each function that you want to receive such a reference, but "explicit is better than implicit" as they say.
Of course, it has all the disadvantage of decorators: another function call slightly degrades performance, and the signature of the wrapped function is no longer visible.
import functools
def gottahavethatfunc(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(func, *args, **kwargs)
return wrapper
The test case illustrates that the decorated function still gets the reference to itself even if you change the name to which the function is bound. This is because you're only changing the binding of the wrapper function. It also illustrates its use with a lambda.
@gottahavethatfunc
def quux(me):
return me
zoom = gottahavethatfunc(lambda me: me)
baz, quux = quux, None
print baz()
print zoom()
When using this decorator with an instance or class method, the method should accept the function reference as the first argument and the traditional self
as the second.
class Demo(object):
@gottahavethatfunc
def method(me, self):
return me
print Demo().method()
The decorator relies on a closure to hold the reference to the wrapped function in the wrapper. Creating the closure directly might actually be cleaner, and won't have the overhead of the extra function call:
def my_func():
def my_func():
return my_func
return my_func
my_func = my_func()
Within the inner function, the name my_func
always refers to that function; its value does not rely on a global name that may be changed. Then we just "lift" that function to the global namespace, replacing the reference to the outer function. Works in a class too:
class K(object):
def my_method():
def my_method(self):
return my_method
return my_method
my_method = my_method()
Solution 5
I just define in the beginning of each function a "keyword" which is just a reference to the actual name of the function. I just do this for any function, if it needs it or not:
def test():
this=test
if not hasattr(this,'cnt'):
this.cnt=0
else:
this.cnt+=1
print this.cnt
Ryan C. Thompson
Updated on December 04, 2020Comments
-
Ryan C. Thompson over 3 years
How can I get a variable that contains the currently executing function in Python? I don't want the function's name. I know I can use
inspect.stack
to get the current function name. I want the actual callable object. Can this be done without usinginspect.stack
to retrieve the function's name and theneval
ing the name to get the callable object?Edit: I have a reason to do this, but it's not even a remotely good one. I'm using plac to parse command-line arguments. You use it by doing
plac.call(main)
, which generates an ArgumentParser object from the function signature of "main". Inside "main", if there is a problem with the arguments, I want to exit with an error message that includes the help text from the ArgumentParser object, which means that I need to directly access this object by callingplac.parser_from(main).print_help()
. It would be nice to be able to say instead:plac.parser_from(get_current_function()).print_help()
, so that I am not relying on the function being named "main". Right now, my implementation of "get_current_function" would be:import inspect def get_current_function(): return eval(inspect.stack()[1][3])
But this implementation relies on the function having a name, which I suppose is not too onerous. I'm never going to do
plac.call(lambda ...)
.In the long run, it might be more useful to ask the author of plac to implement a print_help method to print the help text of the function that was most-recently called using plac, or something similar.
-
Glenn Maynard over 13 yearsThe problem is that redefining the function is common practice in Python, with (but certainly not limited to) decorators. Whether that matters depends on what you're actually doing, of course, but if you really want the function currently executing, then that's not it.
-
Glenn Maynard over 13 yearsA few issues come to mind, but the most critical is threads; multiple threads can call the function in parallel, and return from it in a different order than they entered. Also, if the global scope redefines
foo
, it probably did so for a reason; if callingg
makes a call to something in the global scope that usesfoo
(the integer), it'll break. I'm fairly certain that the only clean way to do this is with language support--a native call that returns the function object. -
Glenn Maynard over 13 yearsNote that if all you (or the OP) want to do is define a recursive function without worrying about it reference to itself being clobbered, you can just nest the recursive function inside another function.
-
jsbueno over 13 years@Glenn: I never said otherwise. :-)
-
aaronasterling over 13 years@Glenn Maynard. Presumably the person that's writing the function that needs to be able to access itself would have control over the enclosing scope to make sure that either (a) such a redefinition did not occur, or (b) if it did occur, the original function was preserved under a different name that could then be used to access it. The bottom of the call stack is easy.
-
aaronasterling over 13 yearsThis doesn't really offer any advantages over just using the name of that the function is defined with. You're getting the name of the code object which will not be the "name" of the function if any redefinition has occurred.
-
Glenn Maynard over 13 yearsIt had to be said. By the way, it'd be safer in edge cases to use a
(code, globals)
tuple as the lambda_cache key. One other thing you'll lose is function attributes. -
Glenn Maynard over 13 yearsAvoiding the need to do that is usually the reason for asking this question in the first place (that and recursive lambdas, I suppose).
-
Ryan C. Thompson over 13 yearsI haven't evaluated your answer for validity yet, but I'm upvoting for "give up the func".
-
Ryan C. Thompson over 13 yearsOk, that's pretty cool. This is exactly what I was looking for.
-
kindall over 13 yearsAs a caveat, this is very CPython-specific.
-
aaronasterling over 13 yearshere's a counter example. Like I said in my answer, there are a lot of corner cases.
-
kindall over 13 yearsAh, yeah, that's where functions share code objects... closures. Hm. For now, added a note that that's a corner case.
-
kindall over 13 yearsCode now detects this and returns
None
rather than the wrong function. I have another idea which I'll work on later. -
bgw about 13 yearsA bit messy, but it shouldn't have any edge cases as far as I can tell.
-
cfi over 12 years@aaronasterling: That's true (read your other notes about closures/decorators). But he does get it without repeating himself. I agree in not using this beyond demoing some example code where it's nice to print function names...
-
cfi about 12 yearsDoesn't work for me in Py3. Used bukzor's answer successfully.
-
mike rodent over 11 yearswhoops! I'm getting Undefined variable on your "this = test" line... and why wouldn't I?
-
Bruno Bronosky over 10 years@mikerodent I suspect you copied and pasted
this=test
but your function is not named test. You have to put the name of the function in there at least once. That is what the OP was trying to avoid. -
chris from local group about 9 years@aaronasterling Sorry if this question is too basic but I was learning from this example. What is the purpose feeding "caller.f_locals.get()" with "func_name" plus "func_name" as seen by globals, instead of just passing func_name alone? Also, what does get() does when you pass two arguments instead of one?
-
bukzor about 9 years@chrisfromlocalgroup:
pydoc dict
docs.python.org/3.5/library/stdtypes.html#dict.get -
chris from local group about 9 years@bukzor It all makes sense now, thanks for your help.
-
Craig S. Anderson over 8 yearsNote from the reference: "This function should be used for internal and specialized purposes only" - because this is not guaranteed to work across different Python implementations.
-
muodov over 8 yearsTrue, but this kind of metadata will always be implementation-specific by definition. This will work in CPython
-
Manfred Radlwimmer about 8 yearsWhy don't you just edit your previous answer instead of creating a new one?
-
Jonathan Eunice almost 7 yearsThis strategy works in both CPython and PyPy, across Python 2.6, 2.7, and 3. The trick is changing the attribute names from
func_code
to__code__
andfunc_globals
to__globals__
. The real gotcha is that it's slow. About 25,000x slower than knowing the function reference. Fine for debugging uses, maybe not so much performance-needing code. The strategy ofeval()
ing a string function name found from the stack is much faster, albeit with its own tradeoffs (closures and such). -
user541686 over 5 yearsUnfortunately this strategy fails miserably with closures... just try
f = lambda: lambda: None
assert f().__code__ is f().__code__
assert f() is not f()
-
Leo Ufimtsev about 5 yearsLOL. This is kinda dumb and simple, but I haven't actually thought of it. This does what I need, thank you.
-
Maggyero almost 4 yearsHi @kindall. Do you have any idea how to solve this?
-
kindall almost 4 years@Maggyero I don't think that's easily solvable using the
gc
approach. You'd have to inspect bound method objects, and there could be any number of those referring to the method of interest. -
Lorem Ipsum over 3 years"give up the func" is a homophonic pun ("func" sounds like "funk"). When the "funk" is substituted, the phrase ceases to be a procedure reference and instead becomes a cultural reference to the hit song of the same name by Parliament Funkadelic. Puns are common in both the music of Parliament and in Python literature. Although a thematic break with the Pythonic tradition of Monty Python, which may tear the roof off the sucker, the homophonic substitution may remove faults, defects or shortcomings. You know, like arthritis, rheumatism or migraines. *puts on sunglasses*