声明:以下内容均来自于《流畅的Python》一书
一、浅拷贝
1、例子一
l1 = [3, [66, 55, 44], (7, 8, 9)]
# 使用 list() 函数对 l1 进行浅拷贝,创建了新的列表 l2。
# 此时 l2 的内容与 l1 相同,但它们在内存中是两个不同的对象。
# 也就是说,对 l1 或 l2 的后续修改不会直接影响对方,除非涉及到嵌套对象的修改(因为浅拷贝只拷贝了最外层对象,嵌套对象是共享引用的)。
l2 = list(l1)
l1.append(100) # 对l1做的是最外层的修改,所以不会影响l2
l1[1].remove(55) # 对l1是对嵌套对象[66, 55, 44]的修改,所以会影响l2
print("l1:", l1) # l1:[3, [66, 44], (7, 8, 9), 100]
print("l2:", l2) # l2:[3, [66, 44], (7, 8, 9)]
从下图可知,嵌套对象列表[66,55,44]和嵌套对象元组(7,8,9)是列表 l1 和 l2 共享引用的
2、例子二
l1 = [3, [66, 55, 44], (7, 8, 9)]
# 使用 list() 函数对 l1 进行浅拷贝,创建了新的列表 l2。
# 此时 l2 的内容与 l1 相同,但它们在内存中是两个不同的对象。
# 也就是说,对 l1 或 l2 的后续修改不会直接影响对方,除非涉及到嵌套对象的修改
# (因为浅拷贝只拷贝了最外层对象,嵌套对象是共享引用的)。
l2 = list(l1)
l2[1] += [33, 22] # 对可变的对象来说,例如 l2[1] 引用的列表,+= 运算符就地修改列表。
# 这次修改在 l1[1] 中也有体现,因为它是 l2[1] 的别名。
print('l1:', l1) # l1: [3, [66, 55, 44, 33, 22], (7, 8, 9)]
print('l2:', l2) # l2: [3, [66, 55, 44, 33, 22], (7, 8, 9)]
l2[2] += (10, 11)# 对元组来说,+= 运算符创建一个新元组,然后重新绑定给变量l2[2]。
# 这等同于 l2[2] = l2[2] + (10, 11)。现在,l1 和 l2中最后位置上的元组不是同一个对象,
print('l1:', l1) # l1: [3, [66, 55, 44, 33, 22], (7, 8, 9)]
print('l2:', l2) # l2: [3, [66, 55, 44, 33, 22], (7, 8, 9, 10, 11)]
从下图可知,对于可变对象列表来说,+= 是就地修改嵌套列表;但对于不可变对象元组来说,它是新建了一个元组,然后重新绑定给 l2[2]。
二、深拷贝与浅拷贝
结合以下代码例子来区分浅拷贝和深拷贝的区别
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
# 创建3个Bus的实例
bus1 = Bus(['Alice', 'Bill', 'Claire', 'David']) # 创建了一个bus对象的实例bus1
bus2 = copy.copy(bus1) # 浅拷贝
bus3 = copy.deepcopy(bus1) # 深拷贝
print(id(bus1), id(bus2), id(bus3)) # 2572463078992 2572463305920 2572463307504,
bus1.drop('Bill')
print(bus2.passengers) # ['Alice', 'Claire', 'David']
print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)) # 2553117940608 2553117940608 2553117940416
# 查看 passengers 属性后发现,bus1 和 bus2 共享同一个列表对象,因为 bus2 是 bus1 的浅拷贝副本。
print(bus3.passengers) # ['Alice', 'Bill', 'Claire', 'David'].
# bus3 是 bus1 的深拷贝副本,因此它的 passengers 属性引用另一个列表。