关于类属性,有一些微妙的问题需要我们注意。
根据
https://docs.python.org/3/tutorial/classes.html:
9.4. 随机备注
如果同一个属性名称在实例和类中都出现了,那么属性查找会优先选择实例。
此外,我观察到,改变类属性的值,这个值的改变会传播回那些没有设置(覆盖)其属性值的实例。这符合上面的文档说明。
让我们来看一些例子。首先,考虑这个类:
class Engine:
started = False;
def start(self):
self.started = True;
让我们实例化它并看看`started`属性的行为:
engine1 = Engine()
print( f"1. engine1.started: {engine1.started}" );
print( f"1. Engine.started: {Engine.started}" );
“如预期一样,两个都是 False:"
1. engine1.started: False
1. Engine.started: False
继续:
engine1.start()
print( f"2. engine1.started: {engine1.started}" );
print( f"2. Engine.started: {Engine.started}" );
输出为:
2. engine1.started: True
2. Engine.started: False
实际上,我看到:
然后属性查找优先考虑实例。
考虑以下实例化:
Engine.started = True
engine2 = Engine()
print( f"3. engine2.started: {engine2.started}" );
print( f"3. Engine.started: {Engine.started}" );
两者都是“True”,这对我们来说应该是有意义的:
3. engine2.started: True
3. Engine.started: True
个人而言,这引出了一个问题:
当类属性的值被改变时,实例属性的值会发生什么变化?也就是说:
engine3 = Engine()
print( f"4. engine3.started: {engine3.started}" );
Engine.started = False
print( f"5. engine3.started: {engine3.started}" );
输出:
4. engine3.started: True
5. engine3.started: False
这是我上面提到的观察结果:
更改类属性的值,该值的更改将传播回那些未设置属性值(被覆盖)的实例。
其次,这是引用上述文档页面的示例,我稍微调整了一下。
class Warehouse:
purpose = 'Storage'
region = 'west'
w1 = Warehouse()
print( "1: ", w1.purpose, w1.region )
输出:
1: Storage west
然后:
w2 = Warehouse()
w2.region = 'east'
print( "2: ", w2.purpose, w2.region )
2: Storage east
Warehouse.region = 'north'
w3 = Warehouse()
print( "3: ", w3.purpose, w3.region )
3: Storage north
这句话的意思是“关于w1和w2呢?”
print( f"4: w1.region: {w1.region}, w2.region: {w2.region}")
输出,我们应该能够弄清原因:
4: w1.region: north, w2.region: east
最终,通过父类更改一个类属性值会将该值的更改向下传播到子类,但反过来不行。
考虑以下例子:
class Engine:
started = False;
def start(self):
self.started = True;
class TwoStrokeEngine(Engine):
pass
class FourStrokeEngine(Engine):
pass
print( f"1. Engine.started: {Engine.started}" );
print( f"1. TwoStrokeEngine.started: {TwoStrokeEngine.started}" );
print( f"1. FourStrokeEngine.started: {FourStrokeEngine.started}\n" );
Engine.started = True
print( f"2. Engine.started: {Engine.started}" );
print( f"2. TwoStrokeEngine.started: {TwoStrokeEngine.started}" );
print( f"2. FourStrokeEngine.started: {FourStrokeEngine.started}\n" );
Engine.started = False
print( f"3. Engine.started: {Engine.started}" );
print( f"3. TwoStrokeEngine.started: {TwoStrokeEngine.started}" );
print( f"3. FourStrokeEngine.started: {FourStrokeEngine.started}\n" );
FourStrokeEngine.started = True
print( f"4. Engine.started: {Engine.started}" );
print( f"4. TwoStrokeEngine.started: {TwoStrokeEngine.started}" );
print( f"4. FourStrokeEngine.started: {FourStrokeEngine.started}" );
输出:
1. Engine.started: False
1. TwoStrokeEngine.started: False
1. FourStrokeEngine.started: False
2. Engine.started: True
2. TwoStrokeEngine.started: True
2. FourStrokeEngine.started: True
3. Engine.started: False
3. TwoStrokeEngine.started: False
3. FourStrokeEngine.started: False
4. Engine.started: False
4. TwoStrokeEngine.started: False
4. FourStrokeEngine.started: True