在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 inspect
def bar():
    print("My name is", inspect.stack()[0][3])

其他回答

print(inspect.stack()[0].function)似乎也可以工作(Python 3.5)。

Python没有在函数本身中访问函数或其名称的特性。它已经被提出,但被否决了。如果你不想自己玩堆栈,你应该使用“bar”或bar。__name__取决于上下文。

给定的拒绝通知为:

此PEP被拒绝。它不清楚应该如何实现,也不清楚在边缘情况下应该有什么精确的语义,而且没有给出足够重要的用例。人们的反应充其量也只是不温不火。

从上面所有使用inspect库的答案来看,所有的都是这样写的:

import inspect

inspect.stack()[0][3]

但是,由于inspect.stack()的返回,[0]是一个NamedTuple,其形式为:

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

可以简单地通过名称调用,即inspect.stack()[0].function

这里可以看到一个小的虚拟例子:

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

当运行时打印:

WARNING - test_train_UGRIZY_noZ -- Not Implemented Yet
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())

测试:

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

输出:

test_class_func_name
test_func_name

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

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