Python类变量修改逻辑与列表操作差异的技术问询
Python类变量行为的常见困惑
这段原本用来讲解Python类变量行为的代码,反而引出了一堆疑问:
Bodyguard类有个类变量protect,是默认包含'the king'的列表。AnnoyingBodyguard和Bureaucrat类会修改这个变量。我们把保护特定人物的守卫称为特定守卫(比如bg_prime、bg_foreign),其他的叫通用守卫(比如bg1、bg2、bg3)。
核心疑问
1. 特定守卫与通用守卫的行为差异
- 特定守卫:类变量的修改只影响修改后初始化的实例,修改前创建的实例不受影响
- 通用守卫:类变量的修改会影响所有实例,不管实例是在修改前还是修改后创建的
为什么特定守卫会有这种前后差异?为什么特定和通用守卫的表现完全不同?
2. 列表操作的不同结果
对于两个列表a和b,大家通常觉得下面三种操作结果应该一致,但在Bodyguard的__init__里使用它们却得到完全不同的效果:
- 重新赋值:
a = a + b - 增量赋值:
a += b - 循环追加:
for x in b: a.append(x)
为什么会这样?
只有重新赋值的结果符合预期,对应的代码示例如下:
class Bodyguard: protect = ['the king'] def __init__(self, *args): if args: self.protect = self.protect + list(args) ################################################################################## bg1 = Bodyguard() bg_prime = Bodyguard('the prime minister') bg_foobar = Bodyguard('the secretary of foo', 'the secretary of bar') assert bg1.protect == ['the king'] assert bg_prime.protect == ['the king', 'the prime minister'] assert bg_foobar.protect == [ 'the king', 'the secretary of foo', 'the secretary of bar' ] ################################################################################## class AnnoyingBodyguard(Bodyguard): Bodyguard.protect = ['his majesty the king'] bg2 = Bodyguard() bg_foreign = Bodyguard('the foreign minister') # 所有通用守卫的国王头衔都更新了 assert bg1.protect == bg2.protect == ['his majesty the king'] # 修改后初始化的特定守卫也受影响 assert bg_foreign.protect == ['his majesty the king', 'the foreign minister'] # 修改前初始化的特定守卫不受影响 assert bg_prime.protect == ['the king', 'the prime minister'] assert bg_foobar.protect == [ 'the king', 'the secretary of foo', 'the secretary of bar' ] ################################################################################## class Bureaucrat: def __init__(self, name): Bodyguard.protect.append(name) malfoy = Bureaucrat('Malfoy') bg3 = Bodyguard() bg_paper = Bodyguard('the secretary of paperwork') # 所有通用守卫的列表都添加了Malfoy assert bg1.protect == bg2.protect == bg3.protect == [ 'his majesty the king', 'Malfoy' ] # 修改后初始化的特定守卫也包含Malfoy assert bg_paper.protect == [ 'his majesty the king', 'Malfoy', 'the secretary of paperwork' ] # 修改前初始化的特定守卫不受影响 assert bg_prime.protect == ['the king', 'the prime minister'] assert bg_foreign.protect == [ 'his majesty the king', 'the foreign minister' ]
编辑说明:基于讨论和解答,优化了代码,消除了特定守卫和通用守卫的行为差异。修改后的Bodyguard类如下:
class Bodyguard: protect = ['the king'] def __init__(self, *args): self.protect = self.protect[:] # 强制为所有守卫创建实例变量副本 if args: self.protect = self.protect + list(args)
内容的提问来源于Stack Exchange,提问作者Watchduck
相关产品推荐
相关产品推荐

