You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测Python中foo()方法执行是由哪一行代码触发的?

区分Python中实例调用和类直接调用实例方法的方式

方法1:解析调用栈的源代码

通过inspect模块获取调用上下文的源代码,直接判断调用形式:

import inspect

class A:
    def foo(self, x):
        # 获取上一级调用帧
        caller_frame = inspect.currentframe().f_back
        # 在调用帧的局部变量中找到指向当前实例的变量名(比如示例中的'a')
        instance_name = None
        for name, val in caller_frame.f_locals.items():
            if val is self:
                instance_name = name
                break
        
        if not instance_name:
            print("无法确定调用来源(可能是表达式调用,比如A().foo())")
            return
        
        # 获取调用行的代码
        caller_code = inspect.getframeinfo(caller_frame).code_context[0].strip()
        class_name = self.__class__.__name__
        
        # 判断调用类型
        if f"{instance_name}.foo(" in caller_code:
            print("调用来自实例调用(对应示例第2行)")
        elif f"{class_name}.foo({instance_name}," in caller_code:
            print("调用来自类直接调用(对应示例第3行)")

# 测试
a = A()
a.foo(123)       # 输出:调用来自实例调用(对应示例第2行)
A.foo(a, 123)    # 输出:调用来自类直接调用(对应示例第3行)

注意事项

  • 该方法依赖调用代码的字面格式,如果调用时使用了复杂表达式(比如(a).foo(123)或self.__class__.foo(a, 123)),需要调整判断逻辑。
  • 如果调用帧中找不到实例对应的变量名(比如直接调用A().foo(123)),会提示无法确定。

方法2:利用方法绑定机制(无需解析源代码)

重写类的__getattribute__方法,给绑定方法添加临时标记,在foo内部检测标记区分调用方式:

import functools

class A:
    def foo(self, x):
        # 检测是否存在绑定调用的标记
        if hasattr(self.foo, '_bound_call_flag'):
            print("调用来自实例调用(对应示例第2行)")
            # 移除标记避免影响后续调用
            delattr(self.foo, '_bound_call_flag')
        else:
            print("调用来自类直接调用(对应示例第3行)")

    def __getattribute__(self, name):
        attr = super().__getattribute__(name)
        # 当访问foo方法时,包装绑定方法添加标记
        if name == 'foo' and callable(attr):
            @functools.wraps(attr)
            def bound_wrapper(*args, **kwargs):
                # 给foo方法添加绑定调用标记
                setattr(self.foo, '_bound_call_flag', True)
                return attr(*args, **kwargs)
            return bound_wrapper
        return attr

# 测试
a = A()
a.foo(123)       # 输出:调用来自实例调用(对应示例第2行)
A.foo(a, 123)    # 输出:调用来自类直接调用(对应示例第3行)

注意事项

  • 该方法不依赖源代码格式,更可靠。
  • 标记是临时的,每次绑定调用后会立即删除,不会影响后续调用。
  • 如果手动给self.foo添加_bound_call_flag属性会导致误判,正常使用场景下不会出现这种情况。

内容的提问来源于stack exchange,提问作者Ярослав Ясиницький

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 06:25:19