先来看一下浅复制和深复制的基本概念。
浅复制是指当对象的字段值被复制时,字段引用的对象不会被复制。
深复制是指对对象事例中字段引用的对象也进行复制的一种方式。
在Python中默认是做浅复制,比如说使用内置的类型构造方法或[:]
复制列表。如果所有元素是不可变的,那么这样操作没有问题,但是如果有可变元素就会导致意想不到的问题,下面通过一个例子来演示一下。
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1) # l1 和 l2 指代不同的列表,但是二者引用同一个列表 [66, 55, 44] 和元组 (7, 8, 9)
l1.append(100) # 把 100 追加到 l1 中,对 l2 没有影响。
l1[1].remove(55) # 移除l1[1]中的55,这对l2有影响,因为l1[1]和l2[1]指向同一个列表
print('l1:', l1)
print('l2:', l2)
l2[1] += [33, 22] # +=运算符就地修改列表,所以这次修改在l1[1]中有影响
l2[2] += (10, 11) # 对于元组, +=运算符是创建一个新元组,然后重新绑定给变量l2[2],这对l1[2]没有影响,l1 和 l2 中最后位置上的元组现在不是同一个对象
print('l1:', l1)
print('l2:', l2)
"""
l1: [3, [66, 44], (7, 8, 9), 100]
l2: [3, [66, 44], (7, 8, 9)]
l1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
"""
下面使用copy
模块提供的deepcopy
和copy
函数为对象进行浅复制和深复制。
先来创建一个类,该类接受乘客序列,并支持移除一个乘客和增加一个乘客。
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)
下面进行测试。
import copy
bus1 = Bus(['Jack', 'Tom', 'Marry'])
# 浅复制
bus2 = copy.copy(bus1)
# 深复制
bus3 = copy.deepcopy(bus1)
# 先来查看各对象的id值
print(id(bus1), id(bus2), id(bus3))
# bus1 Marry下车, 观察bus2和bus3的乘客列表的变化
bus1.drop('Marry')
print(bus2.passengers, bus3.passengers)
# 下面看一下各实例中乘客列表对象的id值
print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers))
"""
57602992 57602896 57602864
['Jack', 'Tom'] ['Jack', 'Tom', 'Marry']
60126312 60126312 57575024
"""
从结果中看出,bus1中的Marry下车,bus2中的Mary也不见了,因为是浅复制,他们指向同一个对象,但是bus3是深复制,所以对其没有什么影响。
另外从各BUS实例中的passengers的id值能够更明显地证明上面所说的。