如何检测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,提问作者Ярослав Ясиницький
相关产品推荐
相关产品推荐

