简体   繁体   English

两个变量似乎指向同一个列表,即使它们应该是唯一的

[英]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_alist_b似乎仍然指向同一个列表,即使我认为我采取了必要的措施来防止这种情况发生。

Why didn't using slice notation to copy my list work? 为什么没有使用切片表示法来复制我的列表工作?

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_alist_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_nlist_n每个子列表list_n ,因此list_alist_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_nlist_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]]
>>> 

When should I use 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()的效率开始变小。

Why is 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.

相关问题 RegEX似乎不匹配,即使它应该匹配 - RegEX doesn't seem to match, even though it should 为什么两个查找素数的算法速度差异很大,即使它们看起来迭代次数相同? - Why do two algorithms for finding primes differ in speed so much even though they seem to do the same number of iterations? TypeError: 'NoneType' object 不支持项目分配,即使变量似乎是正确的 - TypeError: 'NoneType' object does not support item assignment, even though variables seem to be correct .pop()似乎在Python中触发TypeError,即使结果应该是一个列表 - .pop() seems to be triggering a TypeError in Python, even though the result should be a list 检查两个变量是否在同一个列表中 - Checking if two variables are in the same list IndexError:列表索引超出范围,即使打印单个列表和索引似乎没有错误 - IndexError: list index out of range even though printing individual lists and index doesn't seem to have an error 列表理解中的指针都相同,即使它们不应该 - pointers all the same in list comprehension even though they shouldn't 即使列表保持大致相同,也有不同的二分搜索输出 - Different binary search outputs even though the list stays about the same 端口好像没开,虽然在听 - Port does not seem to be open, even though listening AttributeError-即使似乎没有属性错误 - AttributeError - Even though there seem to be no attribute error
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM