Rob
Rob

Reputation: 8070

Determine function name from within that function

Is there a way to determine a function's name from within the function?

def foo():
    print("my name is", __myname__)  # <== how do I calculate this at runtime?

In the example above, the body of foo will somehow access the function name "foo" without hard-coding it. The output would be:

>>> foo()
my name is foo

Upvotes: 784

Views: 517930

Answers (27)

John Forbes
John Forbes

Reputation: 1354

I recently tried to use the above answers to access the docstring of a function from the context of that function but as the above questions were only returning the name string it did not work.

Fortunately I found a simple solution. If like me, you want to refer to the function rather than simply get the string representing the name you can apply eval() to the string of the function name.

import sys
def foo():
    """foo docstring"""
    print(globals()[sys._getframe().f_code.co_name].__doc__)

Upvotes: 5

Rosh Oxymoron
Rosh Oxymoron

Reputation: 21045

If you don't want to play with the stack yourself, you should either use "bar" or bar.__name__ depending on context.

Python doesn't have a feature to access the function or its name within the function itself. A magic __function__ had been proposed for Python 3.0 but rejected. See PEP 3130 – Access to Current Module/Class/Function.

The given rejection notice is:

This PEP is rejected. It is not clear how it should be implemented or what the precise semantics should be in edge cases, and there aren't enough important use cases given. response has been lukewarm at best.

Upvotes: 302

Ad&#225;n Escobar
Ad&#225;n Escobar

Reputation: 4703

implementing your own decorator

#foo.py

from mydecorators import *

@print_my_name
def bar():
    #do something else
    pass
#in terminal: my name is: bar

#mydecorators.py

def resolve_function(func):
    #in case annotated func is an staticmethod
    if isinstance(func,staticmethod):
        return func.__func__
    return func

def print_my_name(func):
    def function_caller(*args,**kwargs):
        _func = resolve_function(func)
        print("my name is: %s" %_func.__name__)
        return _func(*args,**kwargs)
    return function_caller

Upvotes: 1

tallamjr
tallamjr

Reputation: 1470

It seems from all the answer above that use the inspect library, all are writing something like:

import inspect

inspect.stack()[0][3]

But, since the return of inspect.stack()[0] is a NamedTuple of the form:

FrameInfo(frame=<frame at 0x103578810, file '<stdin>', line 1, code <module>>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)

One can simply call by the name, i.e. inspect.stack()[0].function

A small dummy example can be seen here:

    def test_train_UGRIZY_noZ(self, architecture, dataset, hyperrun, wloss):
        log.warning(f"{inspect.stack()[0].function} -- Not Implemented Yet")
        pass

Which when run prints:

WARNING - test_train_UGRIZY_noZ -- Not Implemented Yet

Upvotes: 6

Pritam Dodeja
Pritam Dodeja

Reputation: 326

@jeff-laughlin's answer is beautiful. I have modified it slightly to achieve what I think is the intent: to trace out the execution of functions, and also to capture the list of arguments as well as the keyword arguments. Thank you @jeff-laughlin!

from functools import wraps                                                                                                                                                                                                     
import time                                                                                                                                                                                                                     
                                                                                                                                                                                                                                
def named(func):                                                                                                                                                                                                                
    @wraps(func)                                                                                                                                                                                                                
    def _(*args, **kwargs):                                                                                                                                                                                                     
        print(f"From wrapper function: Executing function named: {func.__name__}, with arguments: {args}, and keyword arguments: {kwargs}.")                                                                                    
        print(f"From wrapper function: {func}")                                                                                                                                                                                 
        start_time = time.time()                                                                                                                                                                                                
        return_value = func(*args, **kwargs)                                                                                                                                                                                    
        end_time = time.time()                                                                                                                                                                                                  
        elapsed_time = end_time - start_time                                                                                                                                                                                    
        print(f"From wrapper function: Execution of {func.__name__} took {elapsed_time} seconds.")                                                                                                                              
        return return_value                                                                                                                                                                                                     
    return _                                                                                                                                                                                                                    
                                                                                                                                                                                                                                
@named                                                                                                                                                                                                                          
def thanks(message, concepts, username='@jeff-laughlin'):                                                                                                                                                                       
    print(f"From inner function: {message} {username} for teaching me about the {concepts} concepts of closures and decorators!")                                                                                               
                                                                                                                                                                                                                                
thanks('Thank you', 'two', username='@jeff-laughlin')                                                                                                                                                                           
print('-'*80)                                                                                                                                                                                                                   
thanks('Thank you', 'two', username='stackoverflow')
print(thanks) 

From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': '@jeff-laughlin'}.

From wrapper function: <function thanks at 0x7f13e6ceaa60>

From inner function: Thank you @jeff-laughlin for teaching me about the two concepts of closures and decorators!

From wrapper function: Execution of thanks took 2.193450927734375e-05 seconds.

--------------------------------------------------------------------------------

From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': 'stackoverflow'}.

From wrapper function: <function thanks at 0x7f13e6ceaa60>

From inner function: Thank you stackoverflow for teaching me about the two concepts of closures and decorators!

From wrapper function: Execution of thanks took 7.152557373046875e-06 seconds.
<function thanks at 0x7f13e6ceaca0>

What is most surprising to me is that there is a way to intercept functions at runtime, inspect them, and take some actions based on this. The other surprising thing is the memory address of the inner function was the same both times. Does anyone know why this is? I have a ways to go before I can understand this decorator/closure magic.

Upvotes: 3

seunggabi
seunggabi

Reputation: 1822

import inspect


def method_name():
    return inspect.stack()[1][3]


def method_name_caller():
    return inspect.stack()[2][3]


def asdf():
    print(method_name_caller())
    print(method_name())


def asdf2():
    print(method_name_caller())
    print(method_name())
    asdf()

Upvotes: 0

user2665694
user2665694

Reputation:

import inspect

def foo():
   print(inspect.stack()[0][3])
   print(inspect.stack()[1][3])  # will give the caller of foos name, if something called foo

foo()

output:

foo
<module_caller_of_foo>

Upvotes: 606

olivecoder
olivecoder

Reputation: 2914

I like the idea of using a decorator but I'd prefer to avoid touching the function arguments. Hence, I'm providing yet another alternative:

import functools

def withname(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        global __name
        __saved_name = globals().get("__name")
        __name = f.__name__
        ret = f(*args, **kwargs)
        __name = __saved_name
        return ret
    return wrapper

@withname
def f():
    print(f"in f: __name=={__name}")
    g()
    print(f"back in f: __name=={__name}")

@withname
def g():
    print(f"in g: __name=={__name}")

We need to save and restore __name when calling the function as consequence of it being a global variable. Calling f() above produces:

in f: __name==f
in g: __name==g
back in f: __name==f

Unfortunately, there is no alternative to the global variable if we don't change the function arguments. Referencing a variable, that is not created in the context of the function, will generate code that would look for a global variable:

>>> def f(): print(__function__)
>>> from dis import dis
>>> dis(f)
  1           0 LOAD_GLOBAL              0 (print)
              2 LOAD_GLOBAL              1 (__function__)
              4 CALL_FUNCTION            1
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

Upvotes: 3

Alex  Granovsky
Alex Granovsky

Reputation: 3324

There are few ways to get the same result:

import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

Note that the inspect.stack calls are thousands of times slower than the alternatives:

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

Update 08/2021 (original post was written for Python2.7)

Python 3.9.1 (default, Dec 11 2020, 14:32:07)
[GCC 7.3.0] :: Anaconda, Inc. on linux

python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
500 loops, best of 5: 390 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
500 loops, best of 5: 398 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
2000000 loops, best of 5: 176 nsec per loop
python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
5000000 loops, best of 5: 62.8 nsec per loop

Upvotes: 300

Manifest Man
Manifest Man

Reputation: 905

Sincesys._getframe().f_back.f_code.co_name does not work at all in python 3.9, following could be used from now:

from inspect import currentframe


def testNameFunction() -> str:
    return currentframe().f_back.f_code.co_name


print(f'function name is {testNameFunction()}(...)')

Result:

function name is testNameFunction(...)

Upvotes: 4

Harry Kearney
Harry Kearney

Reputation: 117

str(str(inspect.currentframe())).split(' ')[-1][:-1]

Upvotes: 0

hobs
hobs

Reputation: 19269

Here's a future-proof approach.

Combining @CamHart's and @Yuval's suggestions with @RoshOxymoron's accepted answer has the benefit of avoiding:

  • _hidden and potentially deprecated methods
  • indexing into the stack (which could be reordered in future pythons)

So I think this plays nice with future python versions (tested on 2.7.3 and 3.3.2):

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

Update: tested on 3.7.10, 3.8.10, and 3.9.5

Upvotes: 18

Vagiz Duseev
Vagiz Duseev

Reputation: 1003

Use __name__ attribute:

# foo.py
def bar():
    print(f"my name is {bar.__name__}")

You can easily access function's name from within the function using __name__ attribute.

>>> def bar():
...     print(f"my name is {bar.__name__}")
...
>>> bar()
my name is bar

I've come across this question myself several times, looking for the ways to do it. Correct answer is contained in the Python's documentation (see Callable types section).

Every function has a __name__ parameter that returns its name and even __qualname__ parameter that returns its full name, including which class it belongs to (see Qualified name).

Upvotes: 29

Jeff Laughlin
Jeff Laughlin

Reputation: 319

This is pretty easy to accomplish with a decorator.

>>> from functools import wraps

>>> def named(func):
...     @wraps(func)
...     def _(*args, **kwargs):
...         return func(func.__name__, *args, **kwargs)
...     return _
... 

>>> @named
... def my_func(name, something_else):
...     return name, something_else
... 

>>> my_func('hello, world')
('my_func', 'hello, world')

Upvotes: 11

karthik r
karthik r

Reputation: 1071

I am not sure why people make it complicated:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))

Upvotes: 20

Genschi
Genschi

Reputation: 134

I suggest not to rely on stack elements. If someone use your code within different contexts (python interpreter for instance) your stack will change and break your index ([0][3]).

I suggest you something like that:

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')

Upvotes: 5

Bjorn
Bjorn

Reputation: 5362

I guess inspect is the best way to do this. For example:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

Upvotes: 31

Mel Viso Martinez
Mel Viso Martinez

Reputation: 658

I do my own approach used for calling super with safety inside multiple inheritance scenario (I put all the code)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

sample usage:

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

testing it :

a = C()
a.test()

output:

called from C
called from A
called from B

Inside each @with_name decorated method you have access to self.__fname__ as the current function name.

Upvotes: 5

Douglas Denhartog
Douglas Denhartog

Reputation: 2054

You can use a decorator:

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'

Upvotes: 8

nordborn
nordborn

Reputation: 455

import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

Test:

a = A()
a.test_class_func_name()
test_func_name()

Output:

test_class_func_name
test_func_name

Upvotes: 13

Pierre Voisin
Pierre Voisin

Reputation: 661

print(inspect.stack()[0].function) seems to work too (Python 3.5).

Upvotes: 20

Lee
Lee

Reputation: 524

import inspect

def whoami():
    return inspect.stack()[1][3]

def whosdaddy():
    return inspect.stack()[2][3]

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

In IDE the code outputs

hello, I'm foo, daddy is

hello, I'm bar, daddy is foo

hello, I'm bar, daddy is

Upvotes: 15

Gino
Gino

Reputation: 1790

This is actually derived from the other answers to the question.

Here's my take:

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

The likely advantage of this version over using inspect.stack() is that it should be thousands of times faster [see Alex Melihoff's post and timings regarding using sys._getframe() versus using inspect.stack() ].

Upvotes: 38

Ron Davis
Ron Davis

Reputation: 1355

functionNameAsString = sys._getframe().f_code.co_name

I wanted a very similar thing because I wanted to put the function name in a log string that went in a number of places in my code. Probably not the best way to do that, but here's a way to get the name of the current function.

Upvotes: 78

xxyzzy
xxyzzy

Reputation: 588

I keep this handy utility nearby:

import inspect
myself = lambda: inspect.stack()[1][3]

Usage:

myself()

Upvotes: 36

bgporter
bgporter

Reputation: 36534

You can get the name that it was defined with using the approach that @Andreas Jung shows, but that may not be the name that the function was called with:

import inspect

def Foo():
   print inspect.stack()[0][3]

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

Whether that distinction is important to you or not I can't say.

Upvotes: 55

cad106uk
cad106uk

Reputation: 509

I found a wrapper that will write the function name

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

This will print

my_funky_name

STUB

Upvotes: 30

Related Questions