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

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

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

当前回答

这实际上是由这个问题的其他答案推导出来的。

以下是我的看法:

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

与使用inspect.stack()相比,这个版本可能的优势是它应该快数千倍[参见Alex Melihoff关于使用sys._getframe()与使用inspect.stack()的文章和计时]。

其他回答

这很容易用装饰器完成。

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

这是一个经得起考验的方法。

将@CamHart和@Yuval的建议与@RoshOxymoron接受的答案结合起来,可以避免:

_hidden和潜在弃用的方法 在堆栈中建立索引(可以在未来的python中重新排序)

所以我认为这对未来的python版本(在2.7.3和3.3.2测试)很有帮助:

from __future__ import print_function
import inspect

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

更新:在3.7.10,3.8.10和3.9.5测试

.f_back.f_code Sincesys._getframe()。Co_name在python 3.9中根本不起作用,下面可以从现在开始使用:

from inspect import currentframe


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


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

结果:

function name is testNameFunction(...)

你可以使用@Andreas Jung显示的方法获得它定义时的名称,但这可能不是调用函数时的名称:

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

这种区别对你来说是否重要,我不能说。

@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>

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