将超类的属性计算委托给其子类



在《Python in a Nutshell》一书中,

作者声称以下代码片段有问题:

class B:
def f(self):
return 23
g = property(f)
class C(B):
def f(self):
return 42
c = C()
print(c.g)                # prints: 23, not 42

正如作者所声称的,解决方案是将属性c.g的计算重定向到类C在超类B级别实现f

class B:
def f(self):
return 23
def _f_getter(self):
return self.f()
g = property(_f_getter)
class C(B):
def f(self):
return 42
c = C()
print(c.g)                # prints: 42, as expected

我不同意,但请帮助我理解为什么作者会这样声称。

我不同意的两个原因。

超类
  1. 很可能在上游模块中,考虑到下游模块中的子类可能会重新实现超类属性的 getter 方法,上游模块实现额外的间接寻址并不理想。

  2. 超类
  3. 属性的计算是在实例化超类实例之后完成的。子类的实例必须在实例化超类的实例之后实例化。因此,如果 C 的编写者没有明确地"声明"C.g是具有不同实现C.f的属性,那么它应该正确地继承该属性B.g,即c.g应该是b.g.

我的问题是:

我的想法是对的,还是作者的主张是对的?

这是对正在发生的事情的更深入的解释。

基本上在你的第一个代码段(这不是很惯用的python)中,你将现有B.f方法绑定到B.g,当C继承g时,它仍然绑定到B.fg基本上是静态链接到B.f

class B:
def f(self):
return 23
g = property(f)  # B.f gets bound to B.g as property

class C(B):
def f(self):
return 42
# C.g not redeclared is still bound to B.f

"更正片段"更不习惯,并绑定一个方法,该方法将从运行它的实例self中检索f方法,这使得它是动态的,因为如果selff方法与B.f不同,那么结果就会改变。但是,我们仍然静态绑定到私有方法。

class B:
def f(self):
return 23
def _f_getter(self):
return self.f()
g = property(_f_getter)  # B._f_getter gets bound to B.g, but B._f_getter uses the f method of self
class C(B):
def f(self):
return 42
# C.g is still bound to B._f_getter but the f method of self has changed

这是使用property的惯用方式,作为另一种方法的装饰器。我们可以使用与上面相同的原理,但可以直接使用g作为名称,而不是声明私有的中间方法。

class B:
def f(self):
return 23
@property
def g(self):  # no binding the B.g property uses f method of self directly
return self.f()
class C(B):
def f(self):
return 42
# C.g uses f method of self which has changed

"超类的属性的计算是在超类的实例化之后完成的。 - 否,该属性与类声明一起声明,并且它与该类中的目标函数相关联。

print(vars(B)['g'])  # <property object at 0x7f5f799ebce0>

"子类的实例必须在实例化超类的实例之后实例化。" - 再次否。这是显而易见的。声明和实例化是不同的过程。

"因此,如果C的编写者没有明确地"声明"C.g是具有不同实现C.f的属性,那么它应该正确地继承属性B.g">- 它在声明阶段继承g属性对象的状态(即绑定B.f函数)。

因此,您自然有有效的选择:

  • 覆盖子类中的属性
  • 或者声明你的初始属性对象来考虑继承,这还不错
<小时 />
class B:
def f(self):
return 23
g = property(lambda self: self.f())
class C(B):
def f(self):
return 42
c = C()
print(c.g)  # 42

我不知道property但你为什么不使用:

class B:
def f(self):
return 23
@property
def g(self):
return self.f()
class C(B):
def f(self):
return 42
c = C()
print(c.g)
print(type(c.g))
# Output
42
int

它和你的初始代码不一样吗?

最新更新