在Python中,如果不使用traceback模块,是否有一种方法可以从函数内部确定函数的名称?

假设我有一个模块foo和一个功能栏。当执行foo.bar()时,是否有一种方法让bar知道bar的名字?或者更好的是,foo。酒吧的名字吗?

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

当前回答

使用__name__属性:

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

你可以使用__name__属性轻松地从函数内部访问函数名。

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

我自己也遇到过这个问题好几次,一直在寻找解决方法。正确答案包含在Python文档中(参见可调用类型部分)。

每个函数都有返回其名称的__name__形参,甚至还有返回其全名的__qualname__形参,包括它属于哪个类(参见限定名称)。

其他回答

@jeff-laughlin的回答很漂亮。我对它进行了轻微的修改,以达到我认为的目的:跟踪函数的执行,并捕获参数列表以及关键字参数。谢谢你@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>

最让我惊讶的是,有一种方法可以在运行时拦截函数,检查它们,并基于此采取一些操作。另一件令人惊讶的事情是内部函数的内存地址两次都是相同的。有人知道这是为什么吗?在我能够理解这个装饰器/闭包魔法之前,我还有一段路要走。

有几种方法可以达到相同的效果:

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)

注意inspect。堆栈调用比替代方法慢数千倍:

$ 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

2021年8月更新(原文章为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
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()

输出:

喷火 < module_caller_of_foo >

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()

我把这个实用工具放在附近:

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

用法:

myself()