问题
最近在用 py3 写一个小脚本,结果遇到了一个问题:不同对象实例对内部属性的操作居然会相互影响。如下,创建实例test1
时的修改居然“蔓延”到了另一个实例test2
上:
class Test:
inter_val = []
def __init__(self, num):
for i in range(num):
self.inter_val.append(i)
print(self.inter_val)
if __name__ == "__main__":
test1 = Test(10)
test2 = Test(10)
执行结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
原因
导致这个问题出现的根本原因其实是 python 的万物皆对象。可以看到,内部属性inter_val
是在声明时就直接分配了内存空间。而在__init__
中直接对其进行操作。这么做乍一看没什么问题,但是实际上,所有由Test
类实例化的对象中的inter_val
数组都指向了同一片内存空间!
通过下面的代码可以更好的理解这个问题:
class Test:
inter_val = []
def __init__(self, num):
# 这里不做操作而是打印其内存地址
print(id(self.inter_val))
if __name__ == "__main__":
test1 = Test(10)
test2 = Test(10)
执行输出
139670768852872
139670768852872
可以看到,两个不同实例的inter_val
完全是同一个数组。不仅如此,所有的复杂类型,如字典、对象等都是这样。只要不是直接对inter_val
进行赋值,都会出现这个问题。
解决方法
至于解决的办法很简单,那就是在__init__
里对需要的属性进行初始化,或者直接啥时候用啥时候初始化,如下:
class Test:
# 不分配空间
inter_val = None
def __init__(self, num):
# 实例化时再分配内存
self.inter_val = []
for i in range(num):
self.inter_val.append(i)
print(self.inter_val)
if __name__ == "__main__":
test1 = Test(10)
test2 = Test(10)
然后就可以看到输出正常了:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
当然,这么做就需要多注意对象属性的空值检查,防止一不小心访问到了None
。