如何克隆列表以避免赋值后意外更改

如何克隆列表以避免赋值后意外更改

技术背景

在Python中,直接使用 new_list = my_list 进行赋值操作时,实际上并没有创建一个新的列表。这种赋值只是复制了列表的引用,而不是实际的列表对象。因此,赋值后 new_listmy_list 都指向同一个列表,对其中一个列表的修改会影响另一个列表。为了避免这种情况,需要真正地复制列表。

实现步骤

浅拷贝

浅拷贝只复制列表本身,即列表中对象的引用,而不复制对象本身。如果列表中的对象是可变的,对这些对象的修改会同时反映在原列表和浅拷贝的列表中。

  • Python 2
    • 使用切片操作:a_copy = a_list[:]
    • 使用 list 构造函数:a_copy = list(a_list)
  • Python 3
    • 使用 list.copy() 方法:a_copy = a_list.copy()

深拷贝

深拷贝会递归地复制列表中的所有对象,创建一个完全独立的新列表。即使原列表中的对象是可变的,对深拷贝列表的修改也不会影响原列表,反之亦然。使用 copy 模块的 deepcopy 函数:

1
2
import copy
a_deep_copy = copy.deepcopy(a_list)

核心代码

浅拷贝示例

1
2
3
4
5
6
7
8
9
10
11
12
13
# 切片操作
old_list = [1, 2, 3]
new_list1 = old_list[:]

# list 构造函数
new_list2 = list(old_list)

# list.copy() 方法
new_list3 = old_list.copy()

print(new_list1) # 输出: [1, 2, 3]
print(new_list2) # 输出: [1, 2, 3]
print(new_list3) # 输出: [1, 2, 3]

深拷贝示例

1
2
3
4
5
6
7
8
import copy

old_list = [[1, 2], [3, 4]]
new_list = copy.deepcopy(old_list)

old_list[0].append(5)
print(old_list) # 输出: [[1, 2, 5], [3, 4]]
print(new_list) # 输出: [[1, 2], [3, 4]]

最佳实践

  • 对于简单列表(列表中的元素都是不可变对象):使用切片操作 list[:]list.copy() 方法进行浅拷贝,因为它们速度快且代码简洁。
  • 对于嵌套列表或包含可变对象的列表:使用 copy.deepcopy() 进行深拷贝,以确保复制的列表与原列表完全独立。

常见问题

直接赋值的问题

1
2
3
4
my_list = [1, 2, 3]
new_list = my_list
new_list[0] = 10
print(my_list) # 输出: [10, 2, 3]

直接赋值只是复制了引用,new_listmy_list 指向同一个列表,对 new_list 的修改会影响 my_list

浅拷贝对嵌套列表的问题

1
2
3
4
old_list = [[1, 2], [3, 4]]
new_list = old_list[:]
new_list[0].append(5)
print(old_list) # 输出: [[1, 2, 5], [3, 4]]

浅拷贝只复制了外层列表,内层列表的引用仍然相同,对嵌套列表的修改会同时影响原列表和浅拷贝的列表。

不建议使用 eval 进行深拷贝

1
2
3
4
5
6
import copy
import timeit

l = list(range(10))
print(min(timeit.repeat(lambda: copy.deepcopy(l)))) # 输出深拷贝的时间
print(min(timeit.repeat(lambda: eval(repr(l))))) # 输出 eval 方法的时间

eval 方法不仅危险(如果评估的内容来自不可信的源),而且不可靠(如果要复制的子元素没有可用于 eval 以重现等效元素的表示形式),并且性能较差。


如何克隆列表以避免赋值后意外更改
https://119291.xyz/posts/2025-05-09.how-to-clone-a-list-to-avoid-unexpected-changes-after-assignment/
作者
ww
发布于
2025年5月9日
许可协议