问题描述
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
。