为什么不使用切片符号来复制我的列表?
您的示例不起作用的原因是您只制作了list_n 的浅拷贝。列表的浅表副本仅复制“顶级”列表。浅复制列表不会复制子列表。通过在list(list.copy()) 上调用copy() 或使用切片表示法(list[:]) 来制作列表的浅拷贝。
在 C 级别,当对列表元素进行浅拷贝时,指向列表的指针(称为 PyObjects)会从一个列表复制到另一个列表。然而,每个子列表的实际指针不会被复制,因此list_a 和list_b 都包含指向确切相同子列表的指针。
说白了,您从未复制list_n 中的每个子列表,因此list_a 和list_b 仍然都包含指向相同子列表的指针。这可以通过创建list_n 的“深拷贝”来解决 - 无论嵌套级别如何,原始列表中每个子列表的副本 - 使用 copy.deepcopy():
>>> from copy import deepcopy
>>>
>>> list_n = [[0,0,0],[0,0,0],[0,0,0]]
>>> list_a = deepcopy(list_n)
>>> list_b = deepcopy(list_n)
>>>
>>> list_a[0][0] = 1
>>> list_a
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> list_b
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>>
什么时候应该使用deepcopy()?
使用deepcopy() 的最大缺点之一是“深度复制”浅嵌套列表需要大量时间。
如果您的列表只是浅嵌套(两到三层深),则应该简单地使用 嵌套列表理解 而不是 deepcopy()。使用这种方法会大大提高效率(感谢@jonrsharpe 指出这一点):
>>> list_n = [[0,0,0],[0,0,0],[0,0,0]]
>>> list_a = [x[:] for x in list_n]
>>> list_b = [x[:] for x in list_n]
使用标准库中的timeit模块可以观察到使用这种方法获得的效率:
>>> import timeit
>>>
>>> setup="from copy import deepcopy; list_n = [[0,0,0],[0,0,0],[0,0,0]]"
>>> timeit.timeit(setup=setup, stmt="deepcopy(list_n)")
24.223977088928223
>>> timeit.timeit(setup=setup, stmt="[x[:] for x in list_n]")
1.2281990051269531
>>>
但是,如果您的列表更深,应该选择使用deepcopy(),即使它看起来有些庞大。人们通常永远不需要牺牲可读性而不是效率。此外,随着列表理解变得越来越复杂,deepcopy() 的效率开始变小。
为什么deepcopy() 这么慢?
deepcopy() 比大多数其他方法慢得多(感谢@Felix 的提问)的原因是因为deepcopy() 比简单的列表理解所做的工作要多得多。与列表理解不同,deecopy() 必须在任意嵌套的列表上工作,可能有多个嵌套级别。因此,在浅嵌套列表中使用它是非常过大的,并且会导致执行时间慢得多。
要更好地了解 deepcopy() 在幕后的作用,您可以查看函数的源代码,因为它是 open source and available to the public for viewing。