第8章:对象引用、可变性和垃圾回收-浅复制和深复制

浅复制

复制列表(或其他可变类型)最简单的办法是使用内置的类型构造方法(还可以用 copy 内置库的 copy() 方法)例如:

l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)

print(l1 == l2)  # True
print(l1 is l2)  # False

第8章:对象引用、可变性和垃圾回收-浅复制和深复制_第1张图片

然而,使用构造方法或 cpoy.copy() 做的都是浅复制(即复制了最外层容器,副本中的元素还是源容器中的元素的引用,见上图)。如果所有的元素都是不可变的,那么这样做没有问题,还能节省内存,但是如果又可变类型的元素,可能会导致意想不到的问题。

例如下面的例子,可以根据注释和内存示意图结合起来一起理解:

# 参考图一,复制列表
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)

# 参考图二,l1 添加元素 100,l1[1] 移除元素 50,因为 l1[1] 是可变类型引用,
# 所以 l2[1] 和 l1[1] 都指向用一个对象,l1[1] 修改之后 l2 的值也会变化;
l1.append(100)
l1[1].remove(55)
print('l1:', l1)  # l1: [3, [66, 44], (7, 8, 9), 100]
print('l2:', l2)  # l2: [3, [66, 44], (7, 8, 9)]

# 参考图三,l2 添加元素,l2[1] 修改会同步修改 l1,但是注意,l2[2] 因为引用的是不可变类型,
# 所以当执行 l2[2] += (10, 11) 时不会修改源容器,而是会新建一个元组容器,所以 l2[2] 的值改变而 l1[2] 的值不变;
l2[1] += [33, 22]
l2[2] += (10, 11)
print('l1:', l1)  # l1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
print('l2:', l2)  # l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

                         图一:                                                              图二:                                                         图三:

第8章:对象引用、可变性和垃圾回收-浅复制和深复制_第2张图片   第8章:对象引用、可变性和垃圾回收-浅复制和深复制_第3张图片     第8章:对象引用、可变性和垃圾回收-浅复制和深复制_第4张图片

深复制 

浅复制没什么问题,但有时我们需要的是深复制(即副本不共享内部对象的引用),我们可以用 copy 模块提供的 deepcopy() 函数为任何对象做深复制。

为了演示 dopy() 和 deepcopy() 的用法,下面示例定义了一个简单的类,用这个类表示运载乘客的校车,在途中会有乘客上下车:

class Bus:

    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)


# 创建实例
bus1 = Bus(['A', 'B', 'C', 'D'])
# 浅复制
bus2 = copy.copy(bus1)
# 深复制
bus3 = copy.deepcopy(bus1)

# 变量各自指向不同的对象
print(id(bus1), id(bus2), id(bus3))  # 4461920832 4461921056 4461922176

# 修改 bus1 乘客,发现 bus2 同步发生变化,bus3 没有变化
bus1.drop('B')
print(bus2.passengers)  # ['A', 'C', 'D']
print(bus3.passengers)  # ['A', 'B', 'C', 'D']

# 实际上 bus1 和 bus2 的 passengers 属性都是指向同一个对象,bus 3是单独的一个对象
print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers))

内存示意图: 

第8章:对象引用、可变性和垃圾回收-浅复制和深复制_第5张图片

此外,有时候深复制可能会太深了,例如对象可能会引用不该复制的外部资源和单例值,我们可以实现特殊方法 __copy____deepcopy__ 来控制 copydeepcopy 的行为。 

你可能感兴趣的:(流畅的Python,深复制,浅复制,copy,deepcopy)