在Python中复制列表时,不同的操作方式会产生不同的结果,尤其是当列表包含嵌套的可变对象时,拷贝行为的差异会直接影响后续的数据操作。很多初学者容易混淆赋值、浅拷贝和深拷贝的概念,导致修改拷贝后的列表时意外影响到原始列表的数据。

直接赋值不是真正的拷贝
很多开发者会误以为用等号赋值就是复制列表,实际上这种方式只是让新的变量指向了原来的列表对象,并没有创建新的列表。我们可以通过id()函数查看两个变量的内存地址来验证这一点。
# 原始列表 original_list = [1, 2, [3, 4]] # 直接赋值 assign_list = original_list # 查看内存地址 print(id(original_list)) print(id(assign_list)) # 修改赋值后的列表 assign_list.append(5) assign_list[2].append(6) # 原始列表也会被修改 print(original_list) # 输出 [1, 2, [3, 4, 6], 5]
从结果可以看到,直接赋值后两个变量的内存地址相同,修改任意一个变量的内容,另一个变量也会同步变化,这种方式并没有实现列表的复制。
浅拷贝的实现方法与特点
浅拷贝会创建一个新的列表对象,但是新列表中的元素只是原始列表元素的引用。如果原始列表中的元素是不可变对象(如数字、字符串、元组),修改新列表的这些元素不会影响原始列表;如果元素是可变对象(如列表、字典),修改这些可变对象的内容会同时影响原始列表和新列表。
浅拷贝的常见实现方式
- 使用列表的
copy()方法 - 使用
copy模块的copy()函数 - 使用切片操作
[:]
下面是三种浅拷贝方式的代码示例:
import copy original_list = [1, 2, [3, 4]] # 方式1:copy()方法 shallow_copy1 = original_list.copy() # 方式2:copy模块的copy函数 shallow_copy2 = copy.copy(original_list) # 方式3:切片操作 shallow_copy3 = original_list[:] # 验证三个新列表都是新的对象 print(id(original_list) != id(shallow_copy1)) # 输出 True print(id(original_list) != id(shallow_copy2)) # 输出 True print(id(original_list) != id(shallow_copy3)) # 输出 True # 修改不可变元素 shallow_copy1[0] = 10 print(original_list) # 输出 [1, 2, [3, 4]],原始列表不受影响 # 修改可变元素(嵌套列表) shallow_copy1[2].append(5) print(original_list) # 输出 [1, 2, [3, 4, 5]],原始列表的嵌套列表被修改
深拷贝的实现方法与特点
深拷贝会递归地复制原始对象中的所有元素,包括嵌套的可变对象,最终生成的新对象和原始对象完全独立,修改新对象的任何内容都不会影响原始对象。深拷贝需要通过copy模块的deepcopy()函数实现。
import copy original_list = [1, 2, [3, 4]] # 深拷贝 deep_copy_list = copy.deepcopy(original_list) # 验证是新对象 print(id(original_list) != id(deep_copy_list)) # 输出 True # 嵌套列表也是新的对象 print(id(original_list[2]) != id(deep_copy_list[2])) # 输出 True # 修改深拷贝后的列表的嵌套元素 deep_copy_list[2].append(5) print(original_list) # 输出 [1, 2, [3, 4]],原始列表完全不受影响 print(deep_copy_list) # 输出 [1, 2, [3, 4, 5]]
浅拷贝与深拷贝的选择建议
在实际开发中,可以根据列表的结构选择拷贝方式:
- 如果列表只包含不可变元素,或者不需要修改列表中的可变元素,使用浅拷贝即可,效率更高。
- 如果列表包含嵌套的可变对象,并且后续需要修改这些嵌套对象,为了避免影响原始列表,必须使用深拷贝。
我们可以通过下面的表格快速对比两种拷贝方式的差异:
| 拷贝方式 | 是否创建新列表对象 | 嵌套可变对象是否独立 | 适用场景 |
|---|---|---|---|
| 直接赋值 | 否 | 否 | 仅需要引用原始列表,不需要独立修改 |
| 浅拷贝 | 是 | 否 | 列表无嵌套可变对象,或不需要修改嵌套可变对象 |
| 深拷贝 | 是 | 是 | 列表有嵌套可变对象,且需要独立修改所有内容 |
注意事项
需要注意的是,深拷贝虽然能完全独立两个对象,但是递归复制的过程会消耗更多的内存和时间,对于结构非常复杂、层级很深的列表,要谨慎使用深拷贝。另外,如果列表中包含循环引用(比如列表的某个元素引用了列表本身),deepcopy()函数会自动处理循环引用,不会陷入无限递归,这一点不需要开发者额外处理。