如何在被调用的方法中获得调用者的方法名?

如何在被调用的方法中获得调用者的方法名?

假设我有两个方法:

def method1(self):
...
a = A.method2()


def method2(self):
...

如果我不想为method1做任何改变,如何获得调用者的名字(在这个例子中,名字是method1)在method2?

178072 次浏览

inspect.getframeinfoinspect中的其他相关函数可以帮助:

>>> import inspect
>>> def f1(): f2()
...
>>> def f2():
...   curframe = inspect.currentframe()
...   calframe = inspect.getouterframes(curframe, 2)
...   print('caller name:', calframe[1][3])
...
>>> f1()
caller name: f1

这种内省旨在帮助调试和开发;出于生产功能的目的而依赖它是不可取的。

较短的版本:

import inspect


def f1(): f2()


def f2():
print 'caller name:', inspect.stack()[1][3]


f1()

(感谢@Alex和Stefaan信托)

我提出了一个稍长的版本,试图构建一个完整的方法名称,包括模块和类。

https://gist.github.com/2151727 (rev 9cccbf)

# Public Domain, i.e. feel free to copy/paste
# Considered a hack in Python 2


import inspect


def caller_name(skip=2):
"""Get a name of a caller in the format module.class.method


`skip` specifies how many levels of stack to skip while getting caller
name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.


An empty string is returned if skipped levels exceed stack height
"""
stack = inspect.stack()
start = 0 + skip
if len(stack) < start + 1:
return ''
parentframe = stack[start][0]


name = []
module = inspect.getmodule(parentframe)
# `modname` can be None when frame is executed directly in console
# TODO(techtonik): consider using __main__
if module:
name.append(module.__name__)
# detect classname
if 'self' in parentframe.f_locals:
# I don't know any way to detect call from the object method
# XXX: there seems to be no way to detect static method call - it will
#      be just a function call
name.append(parentframe.f_locals['self'].__class__.__name__)
codename = parentframe.f_code.co_name
if codename != '<module>':  # top level usually
name.append( codename ) # function or a method


## Avoid circular refs and frame leaks
#  https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack
del parentframe, stack


return ".".join(name)

这似乎很有效:

import sys
print sys._getframe().f_back.f_code.co_name

上面的东西混合在一起。但这是我的尝试。

def print_caller_name(stack_size=3):
def wrapper(fn):
def inner(*args, **kwargs):
import inspect
stack = inspect.stack()


modules = [(index, inspect.getmodule(stack[index][0]))
for index in reversed(range(1, stack_size))]
module_name_lengths = [len(module.__name__)
for _, module in modules]


s = '{index:>5} : {module:^%i} : {name}' % (max(module_name_lengths) + 4)
callers = ['',
s.format(index='level', module='module', name='name'),
'-' * 50]


for index, module in modules:
callers.append(s.format(index=index,
module=module.__name__,
name=stack[index][3]))


callers.append(s.format(index=0,
module=fn.__module__,
name=fn.__name__))
callers.append('')
print('\n'.join(callers))


fn(*args, **kwargs)
return inner
return wrapper

使用:

@print_caller_name(4)
def foo():
return 'foobar'


def bar():
return foo()


def baz():
return bar()


def fizz():
return baz()


fizz()

输出是

level :             module             : name
--------------------------------------------------
3 :              None              : fizz
2 :              None              : baz
1 :              None              : bar
0 :            __main__            : foo

我找到了一种方法,如果你要跨越类,并且想要方法所属的类和方法。这需要一些提取工作,但它是有意义的。这在Python 2.7.13中有效。

import inspect, os


class ClassOne:
def method1(self):
classtwoObj.method2()


class ClassTwo:
def method2(self):
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 4)
print '\nI was called from', calframe[1][3], \
'in', calframe[1][4][0][6: -2]


# create objects to access class methods
classoneObj = ClassOne()
classtwoObj = ClassTwo()


# start the program
os.system('cls')
classoneObj.method1()

代码:

#!/usr/bin/env python
import inspect


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


def caller1():
print "inside: ",called()


def caller2():
print "inside: ",called()
    

if __name__=='__main__':
caller1()
caller2()

输出:

shahid@shahid-VirtualBox:~/Documents$ python test_func.py
inside:  caller1
inside:  caller2
shahid@shahid-VirtualBox:~/Documents$

我将使用inspect.currentframe().f_back.f_code.co_name。它的用法在之前的回答中没有涉及,主要有三种类型:

  • 一些先前的答案使用inspect.stack,但已知它太
  • 一些先前的答案使用sys._getframe,这是一个内部私有函数,因为它的前导下划线,因此隐含地不鼓励使用它。
  • 之前的一个答案使用了inspect.getouterframes(inspect.currentframe(), 2)[1][3],但它完全不清楚[1][3]正在访问什么。
import inspect
from types import FrameType
from typing import cast




def demo_the_caller_name() -> str:
"""Return the calling function's name."""
# Ref: https://stackoverflow.com/a/57712700/
return cast(FrameType, cast(FrameType, inspect.currentframe()).f_back).f_code.co_name




if __name__ == '__main__':
def _test_caller_name() -> None:
assert demo_the_caller_name() == '_test_caller_name'
_test_caller_name()

注意,cast(FrameType, frame)用于满足mypy


确认:1313e对回答的注释。

嘿,伙计,我曾经为我的应用程序做了3个没有插件的方法,也许这可以帮助你,它对我有用,所以可能对你也有用。

def method_1(a=""):
if a == "method_2":
print("method_2")


if a == "method_3":
print("method_3")




def method_2():
method_1("method_2")




def method_3():
method_1("method_3")




method_2()

您可以使用装饰器,而不必使用stacktrace

如果您想在类中修饰一个方法

import functools


# outside ur class
def printOuterFunctionName(func):
@functools.wraps(func)
def wrapper(self):
print(f'Function Name is: {func.__name__}')
func(self)
return wrapper


class A:
@printOuterFunctionName
def foo():
pass

你可以删除functoolsself,如果它是过程性的

Python的Logging库使用查找呼叫者信息来替代sys._getframe()。这个想法是这样的:

  1. 引发异常

  2. 立即在Except子句中捕获它

  3. 使用sys.exc_info获取回溯帧(tb_frame)。

  4. from tb_frame获得上一个调用者的帧使用f_back

  5. 从上一个调用者的帧获得在该帧中执行的代码对象。

    在我们的示例代码中,将执行method1(而不是method2)。

  6. 从所获得的代码对象中,获取对象的名称——这是示例中调用方方法的名称。

下面是解决问题的示例代码:

def method1():
method2()


def method2():
try:
raise Exception
except Exception:
frame = sys.exc_info()[2].tb_frame.f_back


print("method2 invoked by: ", frame.f_code.co_name)


# Invoking method1
method1()

输出:

method2 invoked by: method1

Frame有各种各样的细节,包括行号、文件名、参数计数、参数类型等等。该解决方案也可以跨类和模块工作。