【发布时间】:2011-02-08 21:26:24
【问题描述】:
Python:如何在被调用方法中获取调用者的方法名?
假设我有两种方法:
def method1(self):
...
a = A.method2()
def method2(self):
...
如果我不想对method1做任何更改,如何在method2中获取调用者的名字(本例中为method1)?
【问题讨论】:
标签: python introspection
Python:如何在被调用方法中获取调用者的方法名?
假设我有两种方法:
def method1(self):
...
a = A.method2()
def method2(self):
...
如果我不想对method1做任何更改,如何在method2中获取调用者的名字(本例中为method1)?
【问题讨论】:
标签: python introspection
inspect.getframeinfo和inspect中的其他相关函数可以帮忙:
>>> 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 Lippen)
【讨论】:
这似乎工作得很好:
import sys
print sys._getframe().f_back.f_code.co_name
【讨论】:
inspect.stack快很多
sys 模块进行大量重构后它可能会失败。
我想出了一个稍长的版本,它尝试构建一个完整的方法名称,包括模块和类。
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)
【讨论】:
stack,否则它仍然会由于循环引用而泄漏帧,如inspect-docs中所述
我会使用 inspect.currentframe().f_back.f_code.co_name。之前的任何答案都没有涵盖它的使用,这些答案主要是以下三种类型之一:
inspect.stack,但众所周知它也使用slow。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 评论answer。
【讨论】:
<module>。每当函数直接按名称调用时,总是会出现这种情况吗?或者有没有会弹出模块名的情况?
上面的东西有点融合。但这是我的破解之道。
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
【讨论】:
modules = [(index, inspect.getmodule(stack[index][0])) for index in reversed(range(1, min(stack_size, len(inspect.stack()))))] 获取模块。
modules = [(index, module) for index in reversed(range(1, min(stack_size, len(stack)))) if index and (module := inspect.getmodule(stack[index][0]))]。这样,您可以确保在您的进一步列表中不会有 Nones。另外我认为添加module_name_lengths.append(len(func.__module__)) 很好,因为格式没有考虑表格中最低的元素。
return fn(*args, **kwargs),因为当前的解决方案会抑制装饰函数的返回并破坏它们。
如果您要跨类并想要方法所属的类和方法,我找到了一种方法。这需要一些提取工作,但它说明了它的意义。这适用于 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()
【讨论】:
嘿,伙计,我曾经为我的应用制作了 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()
【讨论】:
代码:
#!/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$
【讨论】:
called() 是被调用的函数,但不是调用 callerN() 的函数