How to get current function into a variable?

30,364

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
Share:
30,364
Ryan C. Thompson
Author by

Ryan C. Thompson

Updated on December 04, 2020

Comments

  • Ryan C. Thompson
    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 using inspect.stack to retrieve the function's name and then evaling 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 calling plac.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
    Glenn Maynard over 13 years
    The 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
    Glenn Maynard over 13 years
    A 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 calling g makes a call to something in the global scope that uses foo (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
    Glenn Maynard over 13 years
    Note 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
    jsbueno over 13 years
    @Glenn: I never said otherwise. :-)
  • aaronasterling
    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
    aaronasterling over 13 years
    This 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
    Glenn Maynard over 13 years
    It 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
    Glenn Maynard over 13 years
    Avoiding 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
    Ryan C. Thompson over 13 years
    I haven't evaluated your answer for validity yet, but I'm upvoting for "give up the func".
  • Ryan C. Thompson
    Ryan C. Thompson over 13 years
    Ok, that's pretty cool. This is exactly what I was looking for.
  • kindall
    kindall over 13 years
    As a caveat, this is very CPython-specific.
  • aaronasterling
    aaronasterling over 13 years
    here's a counter example. Like I said in my answer, there are a lot of corner cases.
  • kindall
    kindall over 13 years
    Ah, yeah, that's where functions share code objects... closures. Hm. For now, added a note that that's a corner case.
  • kindall
    kindall over 13 years
    Code now detects this and returns None rather than the wrong function. I have another idea which I'll work on later.
  • bgw
    bgw about 13 years
    A bit messy, but it shouldn't have any edge cases as far as I can tell.
  • cfi
    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
    cfi about 12 years
    Doesn't work for me in Py3. Used bukzor's answer successfully.
  • mike rodent
    mike rodent over 11 years
    whoops! I'm getting Undefined variable on your "this = test" line... and why wouldn't I?
  • Bruno Bronosky
    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
    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
    bukzor about 9 years
    @chrisfromlocalgroup: pydoc dict docs.python.org/3.5/library/stdtypes.html#dict.get
  • chris from local group
    chris from local group about 9 years
    @bukzor It all makes sense now, thanks for your help.
  • Craig S. Anderson
    Craig S. Anderson over 8 years
    Note 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
    muodov over 8 years
    True, but this kind of metadata will always be implementation-specific by definition. This will work in CPython
  • Manfred Radlwimmer
    Manfred Radlwimmer about 8 years
    Why don't you just edit your previous answer instead of creating a new one?
  • Jonathan Eunice
    Jonathan Eunice almost 7 years
    This 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__ and func_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 of eval()ing a string function name found from the stack is much faster, albeit with its own tradeoffs (closures and such).
  • user541686
    user541686 over 5 years
    Unfortunately 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
    Leo Ufimtsev about 5 years
    LOL. This is kinda dumb and simple, but I haven't actually thought of it. This does what I need, thank you.
  • Maggyero
    Maggyero almost 4 years
    Hi @kindall. Do you have any idea how to solve this?
  • kindall
    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
    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*