You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:02:08