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

如何在通过super()调用的描述符中获取基类?

如何在描述符中获取被装饰方法的原始定义类

代码执行逻辑拆解

当执行print(B().f)时,整个流程分为两步:

  1. 第一次调用描述符的__get__(B类的f)
    • 创建B类实例B(),访问其f属性时,触发B类中f属性对应的描述符d的__get__方法。
    • 此时instance是B的实例,owner参数为B类(属性从B实例访问,上下文属于B类),随后调用B类的f方法。
  2. 第二次调用描述符的__get__(A类的f)
    • B类的f方法中执行super().f,super()返回针对B实例的超类代理对象,该代理会在B的MRO链中查找父类属性。
    • 找到A类的f描述符后,调用其__get__方法时,Python会将B类作为owner参数传入(super代理的上下文是子类B,用于维持继承调用的上下文一致性),因此两次__get__的owner均为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:35:29