在《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
我不同意,但请帮助我理解为什么作者会这样声称。
我不同意的两个原因。
超类很可能在上游模块中,考虑到下游模块中的子类可能会重新实现超类属性的 getter 方法,上游模块实现额外的间接寻址并不理想。
超类属性的计算是在实例化超类实例之后完成的。子类的实例必须在实例化超类的实例之后实例化。因此,如果 C 的编写者没有明确地"声明"
C.g
是具有不同实现C.f
的属性,那么它应该正确地继承该属性B.g
,即c.g
应该是b.g
.
我的问题是:
我的想法是对的,还是作者的主张是对的?
这是对正在发生的事情的更深入的解释。
基本上在你的第一个代码段(这不是很惯用的python)中,你将现有B.f
方法绑定到B.g
,当C
继承g
时,它仍然绑定到B.f
。g
基本上是静态链接到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
方法,这使得它是动态的,因为如果self
的f
方法与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
它和你的初始代码不一样吗?