为什么这个父类 setter 调用 use type(self) 而不是 self?

问题描述

Python @property inheritance the right way 解释了如何调用父 setter。

class Number:
    def __init__(self):
        self._value = None
    
    @property
    def value(self):
        assert self._value is not None
        return self._value

    @value.setter
    def value(self,new_value):
        self._value = new_value


class Integer(Number):
    @property
    def value(self):
        return super().value

    @value.setter
    def value(self,new_value):
        _value = int(new_value)
        super(Integer,type(self)).value.fset(self,_value) # <----- OK with using type(self)
        # super(Integer,self).value.fset(self,_value)     # <----- Assert error with self
        
i = Integer()
i.value = 1             # cause assertion error with "super(Integer,self)"
print(i.value) 

问题

使用 super(Integer,_value) 时,i.value = 1 按预期调用 setter。

对于 super(Integer,_value)i.value = 1 调用 getter 而不是 setter,从而导致断言错误

AssertionError                            Traceback (most recent call last)
<ipython-input-8-2c57a07c128d> in <module>
     35 
     36 i = Integer()
---> 37 i.value = 1
     38 print(i.value)

<ipython-input-8-2c57a07c128d> in value(self,new_value)
     32         _value = int(new_value)
     33         #super(Integer,_value)
---> 34         super(Integer,_value)
     35 
     36 i = Integer()

<ipython-input-8-2c57a07c128d> in value(self)
     10     @property
     11     def value(self):
---> 12         assert self._value is not None
     13         return self._value

问题

尽管调用 super(Integer,_value),但请帮助理解为什么 fset 转到 getter 而不是 setter。阅读文档和文章,在我看来,传递对象 self 而不是类型/类 type(self) 是访问绑定到实例本身的方法的正确方法,但它不起作用。>

super([type[,object-or-type]])

object-or-type 决定了方法解析顺序是 搜索搜索从紧跟在类型之后的类开始。

例如,如果对象或类型的 mro 是 D -> B -> C -> A -> object 并且 type 的值为 B,然后 super() 搜索 C -> A -> 对象。

object-or-type 的 mro 属性列出了方法 getattr() 和 super() 使用的分辨率搜索顺序。这 属性是动态的,可以在继承层次结构中改变 已更新。

Supercharge Your Classes With Python super()

在 Python 3 中, super(Square,self) 调用等效于 无参数 super() 调用。第一个参数指的是子类 Square,而第二个参数指的是一个 Square 对象,在 这种情况,就是自我。您也可以使用其他类调用 super()

    def surface_area(self):
        face_area = super(Square,self).area()
        return face_area * 6

    def volume(self):
        face_area = super(Square,self).area()
        return face_area * self.length 

第二个参数呢?记住,这是一个对象 用作第一个参数的类的实例。例如, isinstance(Cube,Square) 必须返回 True。

通过包含一个实例化的对象,super() 返回一个绑定方法: 绑定到对象的方法,它赋予方法 对象的上下文,例如任何实例属性。如果这个参数是 不包括,返回的方法只是一个函数,没有关联 使用对象的上下文。

解决方法

super(Integer,self).value.fset(self,_value)(或更简单的等价物 super().value.fset(self,_value))的问题甚至在您到达 fset 之前就已发生。描述符协议参与对实例的所有查找,使其只需执行 super(Integer,self).value(或 super().value)即可调用 getter。这就是为什么您继承的 getter 首先起作用的原因;它调用了 property 描述符,并获得了它产生的值。

为了绕过描述符协议(更准确地说,从实例级调用移动到类级调用,其中 property 在类级场景中没有任何特殊作用),您需要对类本身执行查找,不是它的一个实例。 super(Integer,type(self)) 调用 super 的形式返回一个 super 绑定在类级别而不是实例级别的对象,允许您检索原始描述符本身,而不是调用 描述符并获取它产生的值。获得原始描述符后,您就可以访问和调用附加到它的 fset 函数。

这与 super 相关时遇到的问题相同。如果您有一个 Number 实例,并且想要直接访问 fset 函数(而不是通过赋值隐式调用它),您必须这样做:

num = Number()
type(num).value.fset(num,1)

因为做:

num.value.fset(num,1)

在检索 num.value 时失败(获取 getter 生成的 None),然后尝试在 fset 上查找 None

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...