Python 如何获取调用函数(不仅仅是它的名字)?
Python how to get the calling function (not just its name)?
我想写一个函数,returns调用函数:
def foo():
return get_calling_function() #should return 'foo' function object
网上有大量示例如何获取调用函数的名称,但没有如何获取实际对象的示例。我提出了以下获取名称的解决方案,然后在调用函数的全局命名空间中查找它。但是,这不适用于 class 函数,因为您还需要 class 名称,而且我认为还有许多其他边缘情况。
from inspect import stack
def get_calling_function():
return stack()[2][0].f_globals[stack()[1][3]]
那么有什么建议可以如何或是否可以编写此函数以使其通用(在 Python 3 上,顺便说一句)?谢谢。
可以从任何代码对象(以及从扩展 module/builtin)进行调用:从 exec
、execfile
、从模块名称 space(在导入期间),来自 class 定义,来自方法 / class 方法 / staticmethod,来自修饰 function/method,来自嵌套函数,... - 所以没有“调用函数” " 总的来说,很难用它做任何好事。
堆栈帧及其代码对象是您可以获得的最通用的 - 并检查属性。
这个在很多情况下都能找到调用函数:
import sys, inspect
def get_calling_function():
"""finds the calling function in many decent cases."""
fr = sys._getframe(1) # inspect.stack()[1][0]
co = fr.f_code
for get in (
lambda:fr.f_globals[co.co_name],
lambda:getattr(fr.f_locals['self'], co.co_name),
lambda:getattr(fr.f_locals['cls'], co.co_name),
lambda:fr.f_back.f_locals[co.co_name], # nested
lambda:fr.f_back.f_locals['func'], # decorators
lambda:fr.f_back.f_locals['meth'],
lambda:fr.f_back.f_locals['f'],
):
try:
func = get()
except (KeyError, AttributeError):
pass
else:
if func.__code__ == co:
return func
raise AttributeError("func not found")
# Usage
def f():
def nested_func():
print get_calling_function()
print get_calling_function()
nested_func()
class Y:
def meth(self, a, b=10, c=11):
print get_calling_function()
class Z:
def methz(self):
print get_calling_function()
z = Z()
z.methz()
return z
@classmethod
def clsmeth(cls):
print get_calling_function()
@staticmethod
def staticmeth():
print get_calling_function()
f()
y = Y()
z = y.meth(7)
z.methz()
y.clsmeth()
##y.staticmeth() # would fail
它发现:
<function f at 0x012E5670>
<function nested_func at 0x012E51F0>
<bound method Y.meth of <__main__.Y instance at 0x01E41580>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method classobj.clsmeth of <class __main__.Y at 0x01F3CF10>>
但是,它可能会找不到函数,或者找到错误的函数,例如,当同一个装饰器用于不同范围内的多个同名函数时,会找到错误的装饰器生成的包装器。
我想写一个函数,returns调用函数:
def foo():
return get_calling_function() #should return 'foo' function object
网上有大量示例如何获取调用函数的名称,但没有如何获取实际对象的示例。我提出了以下获取名称的解决方案,然后在调用函数的全局命名空间中查找它。但是,这不适用于 class 函数,因为您还需要 class 名称,而且我认为还有许多其他边缘情况。
from inspect import stack
def get_calling_function():
return stack()[2][0].f_globals[stack()[1][3]]
那么有什么建议可以如何或是否可以编写此函数以使其通用(在 Python 3 上,顺便说一句)?谢谢。
可以从任何代码对象(以及从扩展 module/builtin)进行调用:从 exec
、execfile
、从模块名称 space(在导入期间),来自 class 定义,来自方法 / class 方法 / staticmethod,来自修饰 function/method,来自嵌套函数,... - 所以没有“调用函数” " 总的来说,很难用它做任何好事。
堆栈帧及其代码对象是您可以获得的最通用的 - 并检查属性。
这个在很多情况下都能找到调用函数:
import sys, inspect
def get_calling_function():
"""finds the calling function in many decent cases."""
fr = sys._getframe(1) # inspect.stack()[1][0]
co = fr.f_code
for get in (
lambda:fr.f_globals[co.co_name],
lambda:getattr(fr.f_locals['self'], co.co_name),
lambda:getattr(fr.f_locals['cls'], co.co_name),
lambda:fr.f_back.f_locals[co.co_name], # nested
lambda:fr.f_back.f_locals['func'], # decorators
lambda:fr.f_back.f_locals['meth'],
lambda:fr.f_back.f_locals['f'],
):
try:
func = get()
except (KeyError, AttributeError):
pass
else:
if func.__code__ == co:
return func
raise AttributeError("func not found")
# Usage
def f():
def nested_func():
print get_calling_function()
print get_calling_function()
nested_func()
class Y:
def meth(self, a, b=10, c=11):
print get_calling_function()
class Z:
def methz(self):
print get_calling_function()
z = Z()
z.methz()
return z
@classmethod
def clsmeth(cls):
print get_calling_function()
@staticmethod
def staticmeth():
print get_calling_function()
f()
y = Y()
z = y.meth(7)
z.methz()
y.clsmeth()
##y.staticmeth() # would fail
它发现:
<function f at 0x012E5670>
<function nested_func at 0x012E51F0>
<bound method Y.meth of <__main__.Y instance at 0x01E41580>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method classobj.clsmeth of <class __main__.Y at 0x01F3CF10>>
但是,它可能会找不到函数,或者找到错误的函数,例如,当同一个装饰器用于不同范围内的多个同名函数时,会找到错误的装饰器生成的包装器。