python 对象污染

2020-05-20  本文已影响0人  HoPGoldy

问题

最近在用 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

上一篇 下一篇

猜你喜欢

热点阅读