如何在通过super()调用的描述符中获取基类?
如何在描述符中获取被装饰方法的原始定义类
代码执行逻辑拆解
当执行print(B().f)时,整个流程分为两步:
- 第一次调用描述符的
__get__(B类的f)- 创建B类实例
B(),访问其f属性时,触发B类中f属性对应的描述符d的__get__方法。 - 此时
instance是B的实例,owner参数为B类(属性从B实例访问,上下文属于B类),随后调用B类的f方法。
- 创建B类实例
- 第二次调用描述符的
__get__(A类的f)- B类的
f方法中执行super().f,super()返回针对B实例的超类代理对象,该代理会在B的MRO链中查找父类属性。 - 找到A类的
f描述符后,调用其__get__方法时,Python会将B类作为owner参数传入(super代理的上下文是子类B,用于维持继承调用的上下文一致性),因此两次__get__的owner均为B。
- B类的
解决方案:利用__set_name__记录原始定义类
不需要重写__getattribute__,Python 3.6及以上版本提供了__set_name__方法——当描述符被赋值给类属性时,该方法会自动触发,此时可以直接获取方法所在的原始定义类并保存到描述符实例中。
修改后的描述符代码:
class d: def __init__(self, method): self.method = method self.original_class = None # 存储方法定义所在的原始类 def __set_name__(self, owner, name): # owner即为描述符绑定的类,也就是方法定义的原始类 self.original_class = owner def __get__(self, instance, owner=None): print(f"instance: {instance}, owner: {owner}, original_class: {self.original_class}, method: {self.method}") return self.method(instance)
运行测试代码:
class A: @d def f(self): return "A" class B(A): @d def f(self): return super().f + "B" print(B().f)
输出示例:
instance: <__main__.B object at 0x...>, owner: <class '__main__.B'>, original_class: <class '__main__.B'>, method: <function B.f at 0x...> instance: <__main__.B object at 0x...>, owner: <class '__main__.B'>, original_class: <class '__main__.A'>, method: <function A.f at 0x...> AB
可以看到,A类的描述符实例已正确记录原始定义类A,B类的描述符实例记录的是B类,完全满足需求。
为什么不需要__getattribute__?
__getattribute__是实例属性访问的全局钩子,重写它会影响所有属性的访问逻辑,极易引入意外bug。而__set_name__是Python为描述符设计的标准绑定机制,专门用于处理描述符与类的关联过程,更优雅且符合Python设计规范,是解决此类问题的最佳实践。
内容的提问来源于stack exchange,提问作者wRAR
相关产品推荐
相关产品推荐

