Python属性调用链场景下如何识别run方法的首个调用主体为b
解答
你可以通过属性访问追踪钩子或者调用栈打印两种方式,确认run方法的查找链起点是实例b。
方法1:用__getattribute__追踪属性访问顺序
Python中实例的__getattribute__方法会在每次访问实例属性时优先触发,我们可以通过重写这个方法打印访问日志,直观看到属性查找的先后顺序:
class A: def run(self): print("run方法执行") class B: a = A() # 重写属性访问钩子,追踪所有属性访问行为 def __getattribute__(self, item): print(f"[属性访问触发] 从B实例访问属性:{item}") return super().__getattribute__(item) b = B() # 执行属性链式获取 run = b.a.run run()
运行输出如下:
[属性访问触发] 从B实例访问属性:a run方法执行
从输出可以看到,访问b.a.run的第一步就是触发b的属性查找请求,目标为a属性,说明整个调用链的发起者就是实例b。
方法2:打印调用栈确认查找路径
如果要完整看到整个调用流程,可以用inspect模块打印调用栈:
import inspect class A: def run(self): print("=== 调用栈信息 ===") for frame_info in inspect.stack(): print(f"执行函数:{frame_info.function}, 对应代码:{frame_info.code_context[0].strip()}") class B: a = A() b = B() run = b.a.run run()
运行后可以在栈信息里看到,最上层的调用触发源就是run = b.a.run这行代码,最先发起属性查找的就是b实例。
补充说明
你给出的(b.run == b.(B.__dict__['a']).run)存在语法错误,Python不支持b.(xxx)的写法,等价的合法写法是b.a.run == B.__dict__['a'].run,这个判断的结果为True:因为b实例本身没有a实例属性,访问a属性时会直接拿到类B的类属性a对应的A实例,两种路径拿到的是同一个绑定方法。
Python的链式属性访问是严格从左到右执行的,b.a.run的执行顺序是:
- 第一步查找
b的a属性:优先找b的实例属性,找不到就找b所属类B的属性,再找不到就查找父类链的属性 - 第二步拿到
a对应的A实例后,再查找这个A实例的run属性,最终拿到绑定方法
所以整个流程的起点必然是最左侧的b实例。
内容的提问来源于stack exchange,提问作者wenzhen
相关产品推荐
相关产品推荐

