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

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

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

当前回答

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

将@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测试

其他回答

这很容易用装饰器完成。

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

我喜欢使用装饰器的想法,但我更喜欢避免触及函数参数。因此,我提供了另一种选择:

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}")

由于__name是一个全局变量,所以在调用函数时需要保存和恢复__name。调用上面的f()会产生:

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

不幸的是,如果我们不改变函数参数,就没有全局变量的替代品。引用一个不是在函数上下文中创建的变量,将生成寻找全局变量的代码:

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

我最近尝试使用上面的答案从该函数的上下文中访问函数的文档字符串,但由于上面的问题只返回名称字符串,因此不起作用。

幸运的是,我找到了一个简单的解决办法。如果像我一样,想要引用函数,而不是简单地获取表示名称的字符串,那么可以对函数名称的字符串应用eval()。

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)

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

将@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测试

我找到了一个包装器,它将写入函数名

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

这将打印

my_funky_name 存根