Python中如何在另一个函数中获取调用者函数名?

179

如果你有两个函数:

def A
def B

如果 A 调用了 B,你能否在 B 内部获取调用 B 的是谁,例如:

def A () :
    B ()

def B () :
    this.caller.name

1
你已经有源代码了,那你为什么还需要这样的东西呢? - S.Lott
41
因为我正在调试第三方应用程序的Python解释器中的代码,而那里没有真正的调试器。 - Joan Venge
1
将此标记为重复项,因为其他问题在浏览量和赞数上更多。 - George Stocker
5个回答

274

您可以使用inspect模块获取所需信息。它的stack方法返回帧记录列表。

  • For Python 2 each frame record is a list. The third element in each record is the caller name. What you want is this:

    >>> import inspect
    >>> def f():
    ...     print inspect.stack()[1][3]
    ...
    >>> def g():
    ...     f()
    ...
    >>> g()
    g
    

  • For Python 3.5+, each frame record is a named tuple so you need to replace

    print inspect.stack()[1][3]
    

    with

    print(inspect.stack()[1].function)
    

    on the above code.


15
至少在Python 3.4上,这行代码是无效的。他们改变了元组的顺序,现在使用了命名元组,所以最好使用inspect.stack()[1].filename。 - timeyyy
6
实际上,您可能需要使用inspect.currentframe().f_back.f_code.co_name,这与Python版本或实现无关。 - 1313e
@1313e:同时,inspect.currentframe()也取决于Python的实现,因为如果你阅读inspect.py的源代码,它们都使用sys._getframe() - Marco Sulla
我发现当仅使用.function时不够明确时,.filename也可以提供帮助。例如:print(inspect.stack()[1].function, inspect.stack()[1].filename) - 10mjg
它是否也可以打印函数的完整路径? - alper
在类的__init__内部,是否有获取相同信息的替代方法? - t3chb0t

38

有两种方法,使用sysinspect模块:

  • sys._getframe(1).f_code.co_name
  • inspect.stack()[1][3]

stack()形式不太易读,而且依赖于实现,因为它调用了sys._getframe(),参见从inspect.py中提取的内容:

def stack(context=1):
    """Return a list of records for the stack above the caller's frame."""
    return getouterframes(sys._getframe(1), context)

1
这个 sys._getframe(1).f_code.co_name 是否比 inspect.stack()[1][3] 更快? - jeffry copps
在急于提问之前,你是否尝试过计时(例如使用timeit)?我认为它更快,因为它似乎不会产生函数调用和两个列表查找。但是在Python中可能会隐藏一些东西,所以最好的方法是使用timeit进行测试。请告诉我们你的发现。 - Eric
我想知道为什么,因为我知道它更快。我不喜欢“似乎”这样的回答。 - jeffry copps
6
这是4000次调用的两者比率。(getframe) 0.00419473648071 : (inspect) 29.3846197128 - jeffry copps
很好的发现!我猜它是使用CPython和Python版本2或3对结果影响不大。感谢您运行基准测试。 - Eric

16

注意(2018年6月):今天,我可能会使用inspect模块,请参见其他回答

可以像下面的示例中使用sys._getframe(1).f_code.co_name

>>> def foo():
...  global x
...  x = sys._getframe(1)
...
>>> def y(): foo()
...
>>> y()
>>> x.f_code.co_name
'y'
>>>  

重要提示:从_getframe方法名称中可以明显看出(嘿,它以下划线开头),它不是一个人们应该盲目依赖的 API 方法。


14
sys._getframe在所有Python实现中都“不能保证存在” - 我认为这一点很重要。 - RWS

9
这对我很有用! :D
>>> def a():
...     import sys
...     print sys._getframe(1).f_code.co_name
...
>>> def b():
...     a()
...
...
>>> b()
b
>>>

这个很好用,但一开始有点困惑,因为你本可以让b()函数输出它自己的名字。其他答案建议使用“inspect.”,但是失败了,因为它没有被定义。我猜作者忘记了一个依赖声明。 - WinEunuuchs2Unix
对我来说,这比inspect.stack()快多了,至少快了1000倍。 - Conchylicultor

6

您可以使用logging模块并在BaseConfig()中指定%(funcName)s选项。

该选项与函数名称相关联。
import logging
logging.basicConfig(
    filename='/tmp/test.log', 
    level=logging.DEBUG, 
    format='%(asctime)s | %(levelname)s | %(funcName)s |%(message)s',
)

def A():
    logging.info('info')

1
你不小心说成了 %(filename)s 选项。应该是你代码示例中的 %(funcName)s :) - ᴠɪɴᴄᴇɴᴛ
1
看起来 logging 在底层使用了 sys._getframe() 方法,可以在这里这里看到。 - djvg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接