Python继承中int与list类变量表现差异原因及统一实现咨询
为什么类变量n和lines行为不同?怎么让lines和n保持一致?
这个问题其实戳中了Python里类变量一个很容易踩的坑——不可变对象和可变对象在类继承中的行为差异,咱们来慢慢捋清楚:
一、先搞懂为什么n和lines表现不一样
1. 关于不可变对象n
n是整数,属于不可变类型。当你调用B.Add('hello')时,cls.n += 1这个操作其实是:
- 先读取父类
A中n的值(0) - 计算
0 + 1 = 1 - 给子类
B创建一个属于它自己的类变量n,并赋值为1
之后B的n就和父类A的n彻底分家了,同理C调用Add时也会生成自己的n,所以最后两个子类的n都是独立的,值为1。
2. 关于可变对象lines
lines是列表,属于可变类型。cls.lines.append(line)这个操作是直接在原列表对象上修改,而不是创建新的列表。
因为B和C一开始都没有定义自己的lines类变量,所以它们会一直引用父类A的那个lines列表。不管是B还是C调用Add,都是在同一个列表里追加元素,最后自然会看到两个子类的lines都包含['hello', 'world']。
二、让lines和n行为一致的解决方案
你不想在每个子类里重新声明lines,那咱们可以在父类里做手脚,让每个子类自动拥有自己的lines:
方法1:用__init_subclass__(推荐,Python3.6+支持)
__init_subclass__是Python3.6新增的钩子,当子类被定义时会自动调用。我们在父类A里添加这个方法,给每个子类初始化独立的lines:
class A(): n = 0 lines = [] @classmethod def Add(cls, line): cls.n += 1 cls.lines.append(line) def __init_subclass__(cls): super().__init_subclass__() cls.lines = [] # 每个子类创建时都初始化自己的lines列表 class B(A): pass class C(A): pass B.Add('hello') C.Add('world') print(B.lines, B.n) # 输出:['hello'] 1 print(C.lines, C.n) # 输出:['world'] 1
方法2:在Add方法中动态检查(兼容旧版本Python)
如果你的Python版本低于3.6,可以在Add方法里检查当前类是否有自己的lines,没有就创建:
class A(): n = 0 lines = [] @classmethod def Add(cls, line): cls.n += 1 # 检查当前类是否拥有独立的lines,没有则创建 if not hasattr(cls, 'lines'): cls.lines = [] cls.lines.append(line) class B(A): pass class C(A): pass B.Add('hello') C.Add('world') print(B.lines, B.n) # 输出:['hello'] 1 print(C.lines, C.n) # 输出:['world'] 1
这样就能让lines和n一样,每个子类都拥有独立的实例啦。
内容的提问来源于stack exchange,提问作者iain
相关产品推荐
相关产品推荐

