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

Why does this parent class setter call use type(self) rather than 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, type(self)).value.fset(self, _value) 时,i.value = 1 按预期调用 setter。

对于 super(Integer, self).value.fset(self, _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, type(self)).value.fset(self, _value)
---> 34         super(Integer, self).value.fset(self, _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, self).value.fset(self, _value)getter 而不是 setter 尽管调用 fset。阅读文档和文章,在我看来传递对象 self 而不是 type/class type(self) 是访问绑定到实例本身的方法的正确方法,但它不起作用。

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

The object-or-type determines the method resolution order to be searched. The search starts from the class right after the type.

For example, if mro of object-or-type is D -> B -> C -> A -> object and the value of type is B, then super() searches C -> A -> object.

The mro attribute of the object-or-type lists the method resolution search order used by both getattr() and super(). The attribute is dynamic and can change whenever the inheritance hierarchy is updated.

Supercharge Your Classes With Python super()

In Python 3, the super(Square, self) call is equivalent to the parameterless super() call. The first parameter refers to the subclass Square, while the second parameter refers to a Square object which, in this case, is self. You can call super() with other classes as well:

    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 

What about the second parameter? Remember, this is an object that is an instance of the class used as the first parameter. For an example, isinstance(Cube, Square) must return True.

By including an instantiated object, super() returns a bound method: a method that is bound to the object, which gives the method the object’s context such as any instance attributes. If this parameter is not included, the method returned is just a function, unassociated with an object’s context.

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

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

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

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

因为在做:

num.value.fset(num, 1)
当您检索 num.value

失败(获取 getter 生成的 None),然后尝试在 None 上查找 fset