Python如何获取调用函数(不仅仅是它的名字)?

Posted

技术标签:

【中文标题】Python如何获取调用函数(不仅仅是它的名字)?【英文标题】:Python how to get the calling function (not just its name)? 【发布时间】:2016-12-28 22:36:30 【问题描述】:

我想写一个返回调用函数的函数:

def foo():
    return get_calling_function() #should return 'foo' function object

网上有很多例子如何获取调用函数的name,但没有如何获取实际对象。我提出了以下解决方案,它获取名称,然后在调用函数的全局命名空间中查找它。但是,这不适用于类函数,因为您还需要类名,并且我认为还有许多其他边缘情况。

from inspect import stack
def get_calling_function():
    return stack()[2][0].f_globals[stack()[1][3]]

那么有什么建议如何或是否可以编写此函数以使其通用(在 Python 3 上,顺便说一句)?谢谢。

【问题讨论】:

你的用例是什么? 我正在编写一个函数,它返回传递给调用它的函数的所有参数的字典,except 对于那些值设置为默认值的函数。我需要调用函数对象,以便可以在其上调用getargspec,以便弄清楚这些默认值是什么。 【参考方案1】:

可以从任何代码对象(以及扩展模块/内置)进行调用:从execexecfile、从模块名称空间(在导入期间)、从类定义内、从方法/类方法内/ staticmethod,来自修饰函数/方法,来自嵌套函数,...... - 所以一般来说没有“调用函数”,很难用它做任何好事。

堆栈帧及其代码对象是您可以获得的最通用的 - 并检查属性。


这个找到很多情况下的调用函数:

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>>

但是,它可能会找不到函数,或者找到错误的函数,例如,当在不同范围内的多个同名函数上使用相同的装饰器时,会找到错误的装饰器生成的包装器。

【讨论】:

这是一个公平的观点,我的函数可以在你提到的那些未定义的情况下例外。但是如果它是从函数调用的,无论是模块还是类级别,那么我想要调用者的函数对象。到目前为止,我在这些堆栈对象中的任何地方都找不到这个对象。 对于帧堆栈上的代码对象,没有到可能定义函数的常规统一链接。我添加了一个查找器演示,它匹配许多典型案例 - 匹配由 func.__code__ == co 验证。可以延长... 哇,太完美了,非常感谢!当然涵盖了我感兴趣的所有案例。

以上是关于Python如何获取调用函数(不仅仅是它的名字)?的主要内容,如果未能解决你的问题,请参考以下文章

Inno Setup - 如何设置文件夹的完全权限,而不仅仅是它的内容

Python如何获取到当前函数名和通过字符串调用函数

Python——lambda函数

Python中的lambda函数

iPhone - 删除整个 viewController,而不仅仅是它的视图

缩放 UIScrollView(不仅是它的内容)