在Python中,实例属性和类属性存放在不同的地方,实例属性保存在实例的__dict__中,而类属性保存在类的__dict__中。默认情况下,修改实例属性不会影响类属性,但我们可以通过一些写法在更新实例属性时同步更新类属性。

为什么直接赋值不会更新类属性
当我们写 instance.x = 10 时,实际是在实例上创建了一个新的实例属性 x,它遮蔽了同名的类属性,类属性本身并没有改变。可以通过下面的代码观察这一现象:
class Demo:
count = 0
d = Demo()
d.count = 5 # 只是给实例加了属性
print(d.count) # 输出 5
print(Demo.count) # 输出 0,类属性未变
print(d.__dict__) # 输出 {'count': 5}
方法一:在方法中显式修改类属性
我们可以在实例方法里通过类名或 __class__ 去修改类属性,这样在更新实例属性的同时也就更新了类属性。
class Counter:
total = 0
def __init__(self, value):
self.value = value
Counter.total += value # 更新类属性
def update(self, new_value):
# 更新实例属性,同时同步类属性
Counter.total -= self.value
self.value = new_value
Counter.total += self.value
c1 = Counter(3)
c2 = Counter(4)
print(Counter.total) # 输出 7
c1.update(10)
print(c1.value) # 输出 10
print(Counter.total) # 输出 14
方法二:使用 property 描述符
利用 property 可以把属性的读写逻辑包装起来,在设置实例属性时自动修改类属性。
class Config:
default_timeout = 30
def __init__(self):
self._timeout = Config.default_timeout
@property
def timeout(self):
return self._timeout
@timeout.setter
def timeout(self, value):
self._timeout = value
Config.default_timeout = value # 同步更新类属性
cfg = Config()
cfg.timeout = 60
print(cfg.timeout) # 输出 60
print(Config.default_timeout) # 输出 60
方法三:使用 __setattr__ 钩子
重写 __setattr__ 可以在每次设置属性时拦截并判断是否要同步类属性,但需要注意避免无限递归。
class Sync:
shared = 0
def __setattr__(self, name, value):
if name == 'data':
Sync.shared = value # 更新类属性
super().__setattr__(name, value) # 调用父类方法避免递归
s = Sync()
s.data = 100
print(Sync.shared) # 输出 100
print(s.data) # 输出 100
注意事项
- 不要误以为通过 self.类属性名 = 值 能修改类属性,这只会创建实例属性。
- 如果类属性是可变对象,如列表,实例修改其内容可能影响所有实例,但不改变引用本身。
- 多继承场景下用 __class__ 修改类属性要小心,可能改到错误的类。
通过上述方式,我们可以在更新实例属性时有意地同步类属性,从而满足统计、共享配置或状态联动等需求。