[英]Two variables seem to point to the same list, even though they should be unique
I was using lists in a program, but I don't understand this following behavior. 我在程序中使用列表,但我不理解以下行为。 I have begun to understand mutability and how it affects variable assignment, but I do not see the problem here: 我已经开始理解可变性以及它如何影响变量赋值,但我没有看到这里的问题:
class Test:
def __init__(self, list_n):
list_a = list_n[:]
list_b = list_n[:]
print(list_a is list_b) # Prints False
print(list_a is list_n) # Prints False
print(list_b is list_n) # Prints False
list_a[0][0] = 1
print(list_a) # Both of these print [[1,0,0][0,0,0][0,0,0]]
print(list_b)
def main():
list_n = [[0,0,0],[0,0,0],[0,0,0]]
test = Test(list_n)
if __name__ == '__main__': main()
Both list_a
and list_b
seem to still point to the same list, even though I thought I took the necessary measures to prevent that from happening. list_a
和list_b
似乎仍然指向同一个列表,即使我认为我采取了必要的措施来防止这种情况发生。
The reason your example did not work is because you only made a shallow copy of list_n
. 您的示例不起作用的原因是因为您只创建了list_n
的浅表副本 。 A shallow copy of a list only copies the "top-level" list. 列表的浅表副本仅复制“顶级”列表。 Shallow copying a list does not copy sublists . 浅复制列表不会复制子列表 。 Shallow copies of lists are made by calling copy()
on the list
( list.copy()
) or using slice notation( list[:]
). 列表的浅副本是通过调用list
上的copy()
( list.copy()
)或使用切片表示法( list[:]
)来创建的。
At the C level, when a shallow copy is done on elements which are lists, pointers to the lists - called PyObject
s - are being copied from one list to another. 在C级别,当对作为列表的元素进行浅拷贝时,指向列表的指针(称为PyObject
s)正从一个列表复制到另一个列表。 The actual pointer for each sublist however, is not copied, and thus list_a
and list_b
both contain pointers to the exact same sublists. 但是,不会复制每个子列表的实际指针,因此list_a
和list_b
都包含指向完全相同的子列表的指针。
To put it plainly, you never made a copy of each sublist in list_n
, and thus list_a
and list_b
still both contain pointers to the same sublists. 说白了,你从来没有在list_n
中list_n
每个子列表的list_n
,因此list_a
和list_b
仍然包含指向相同子列表的指针。 This can be fixed by creating a "deepcopy" of list_n
- a copy of each sublist in the original list regardless of the nesting level - using copy.deepcopy()
: 这可以通过创建list_n
“ list_n
复制”来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()
? One of the biggest drawbacks to using deepcopy()
is that it takes a substantial amount of time to "deepcopy" shallowly nested lists. 使用deepcopy()
的最大缺点之一是“深度复制”浅层嵌套列表需要花费大量时间。
If your list is only shallowly nested(two to three levels deep), one should simply use a nested list comprehension instead of deepcopy()
. 如果您的列表只是浅层嵌套(深度为两到三层),则应该只使用嵌套列表 deepcopy()
而不是deepcopy()
。 Using this method would be substantially more efficient(thanks to as @jonrsharpe for pointing this out): 使用这种方法会更有效率(感谢@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]
The efficency gained by using this method can be observed using the timeit
module in the standard library: 使用此方法获得的效率可以使用标准库中的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
>>>
However, if your list is any deeper, one should opt to use deepcopy()
instead, even if it seems somewhat bulky. 但是,如果您的列表更深,则应选择使用deepcopy()
,即使它看起来有些笨重。 One usually never needs to sacrifice readability over efficiency. 通常永远不需要牺牲可读性而不是效率。 Furthermore, as the list comprehension becomes increasingly complex, the efficiency over deepcopy()
begins to grow smaller. 此外,随着列表理解变得越来越复杂, deepcopy()
复制deepcopy()
的效率开始变小。
deepcopy()
so slow? 为什么deepcopy()
这么慢? The reason deepcopy()
is so much slower than most other methods(thanks to @Felix for asking), is because deepcopy()
does much more work than a simple list comprehension. deepcopy()
比大多数其他方法慢得多(感谢@Felix的问题),因为deepcopy()
比简单的列表理解做了更多的工作。 unlike the list comprehension, deecopy()
must work on arbitrarily nested list, possible with many levels of nesting. 与列表deecopy()
不同, deecopy()
必须在任意嵌套列表上工作,可能有deecopy()
嵌套。 Thus, it is extreme overkill to use it on shallowly nested list, and will result in much slower execution time. 因此,在浅嵌套列表中使用它是极端的过度杀伤,并且会导致执行时间慢得多。
To get a better idea of what deepcopy()
does behind the scenes you can take a look at the source code for the function, as it is open source and available to the public for viewing . 为了更好地了解deepcopy()
在幕后的作用,您可以查看该函数的源代码,因为它是开源的,可供公众查看 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.