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

Python 两个变量似乎指向同一个列表,尽管它们应该是唯一的,python,Python,我在程序中使用列表,但我不理解下面的行为。我已经开始了解易变性以及它如何影响变量赋值,但我看不到这里的问题: 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

我在程序中使用列表,但我不理解下面的行为。我已经开始了解易变性以及它如何影响变量赋值,但我看不到这里的问题:

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()
list_a
list_b
似乎仍然指向同一个列表,尽管我认为我已经采取了必要的措施来防止这种情况发生

为什么不使用切片表示法复制我的列表? 您的示例不起作用的原因是,您只制作了
list\n
的一个浅拷贝。列表的浅层副本仅复制“顶级”列表。浅层复制列表不会复制子列表。通过调用
list
list.copy()
)上的
copy()
)或使用切片表示法(
list[:]
)创建列表的浅层副本

在C级,当对列表元素进行浅层复制时,指向列表的指针(称为
PyObject
s)正在从一个列表复制到另一个列表。但是,不会复制每个子列表的实际指针,因此
list_a
list_b
都包含指向完全相同的子列表的指针

简单地说,您从未在
list\u n
中复制过每个子列表,因此
list\u a
list\u b
仍然包含指向相同子列表的指针。可以通过创建
列表的“deepcopy”\n
来解决这一问题,即创建原始列表中每个子列表的副本,而不考虑嵌套级别,使用:

我应该什么时候使用
deepcopy()
? 使用
deepcopy()
的最大缺点之一是“deepcopy”浅嵌套列表需要大量时间

如果您的列表只是浅嵌套的(两到三层),那么应该简单地使用嵌套列表理解,而不是
deepcopy()
。使用这种方法将大大提高效率(感谢as@jornsharpe指出这一点):

通过使用标准库中的模块可以观察使用此方法获得的效率:

>>> 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()
比简单的列表理解做了更多的工作。与列表理解不同,
deecompy()
必须在任意嵌套的列表上工作,可能有许多嵌套级别。因此,在嵌套较浅的列表上使用它是非常过分的,并且会导致执行时间大大降低


为了更好地了解
deepcopy()
在幕后做了什么,您可以查看函数的源代码,因为它是这样的。

列表片是一个浅拷贝。为了记录在案,可变性不会影响赋值。让我们来看看。因为它只是一个单层的深层和同质的,我怀疑
[l[:]代表列表中的l\u n]
将比
deepcopy
更高效。嵌套列表comp对于更深层的结构来说会很快变得丑陋,这是真的!不过,它似乎快了两个数量级,所以我想我应该提一下。@jonrsharpe我一点也不介意!事实上,你是完全正确的。使用
timeit
模块,计时
deepcopy()
大约需要30.5秒,而您的方法只需要大约1秒!感谢您的更新。为什么
deepcopy
这么慢?它实际上并没有比列表理解做更多的工作,是吗?如果你为你不使用的东西付费,它的设计就很糟糕。他们应该在C中实现这一点。以这种速度,cPickle/json甚至比deepcopy更快。。。
>>> 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]
>>> 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
>>>