为啥这个父类 setter 调用使用 type(self) 而不是 self?
Posted
技术标签:
【中文标题】为啥这个父类 setter 调用使用 type(self) 而不是 self?【英文标题】:Why does this parent class setter call use type(self) rather than self?为什么这个父类 setter 调用使用 type(self) 而不是 self? 【发布时间】:2021-06-05 08:05:51 【问题描述】: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
按预期调用设置器。
对于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(self)
是访问绑定到实例本身的方法的正确方法,但它不起作用。
super([type[, object-or-type]])
对象或类型决定方法解析顺序 搜索。搜索从类型之后的类开始。
例如,如果 mro 的 object-or-type 是 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() 返回一个绑定的方法:a 绑定到对象的方法,它赋予该方法 对象的上下文,例如任何实例属性。如果这个参数是 不包含,返回的方法只是一个函数,无关联 带有对象的上下文。
【问题讨论】:
【参考方案1】:super(Integer, self).value.fset(self, _value)
(或更简单的等价物super().value.fset(self, _value)
)的问题甚至在您到达fset
之前就已出现。描述符协议参与实例上的所有查找,只需执行super(Integer, self).value
(或super().value
)即可使其调用getter。这就是为什么您继承的吸气剂首先起作用的原因;它调用了property
描述符,并得到了它产生的值。
为了绕过描述符协议(更准确地说,从实例转移到类级别调用,property
s 在类级别场景中没有做任何特别的事情),您需要对类本身执行查找,而不是它的实例。 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
)时失败,然后尝试在 None
上查找 fset
。
【讨论】:
"super(Integer, type(self)) 调用 super 的形式,它返回绑定在类级别而不是实例级别的超级对象,允许您检索原始描述符本身,而不是调用描述符并获取它产生的值。” - 好吧,它仍然调用描述符协议,但是描述符协议的类版本,而不是实例版本。这对于@classmethod
之类的东西很重要,但是对于属性,在类上查找只会返回属性对象。
@user2357112supportsMonica:是的,我不想在主要答案的正文中陷入困境。还有一种情况,instance 级描述符协议仍然被调用 if 类的元类定义了这样的描述符(因为所有类本身都是它们元类的实例;所有的都是海龟下降)。我为你的笔记添加了一个括号。
@ShadowRanger,非常感谢您的回答,很抱歉尚未接受,因为我需要时间来消化内容和描述符协议。以上是关于为啥这个父类 setter 调用使用 type(self) 而不是 self?的主要内容,如果未能解决你的问题,请参考以下文章
java中,当实例化子类时会递归调用父类中的构造方法。这个说法对么?为啥
为啥`type.__new__`调用`__init_subclass__`?