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

2024-04-26 00:36:45 发布

您现在位置: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
        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和{}似乎仍然指向同一个列表,尽管我认为我已经采取了必要的措施来防止这种情况的发生。在


Tags: testself程序false列表initismain
1条回答
网友
1楼 · 发布于 2024-04-26 00:36:45

为什么不用切片表示法复制我的列表?在

您的示例不起作用的原因是您只对list_n进行了一个浅拷贝。列表的浅拷贝只复制“顶层”列表。浅层复制列表不会复制子列表。通过在listlist.copy())上调用copy()或使用切片表示法(list[:])来生成列表的浅拷贝。在

在C级别,当对列表元素执行浅层复制时,指向列表的指针(称为PyObjects)将从一个列表复制到另一个列表。但是,每个子列表的实际指针不会被复制,因此list_a和{}都包含指向相同子列表的指针。在

简单地说,您从来没有复制过list_n中的每个子列表,因此list_a和{}仍然包含指向相同子列表的指针。可以通过使用^{}创建list_n的“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()的最大缺点之一是“深度复制”浅嵌套列表需要大量时间。在

如果你的列表只是浅层嵌套的(2到3层深),你应该简单地使用嵌套列表理解来代替deepcopy()。使用此方法将大大提高效率(感谢as@jornsharpe指出这一点):

^{pr2}$

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

^{3}$

但是,如果您的列表更深入,那么应该选择使用deepcopy(),即使它看起来有点庞大。人们通常不需要牺牲可读性而非效率。此外,当列表理解变得越来越复杂时,deepcopy()上的效率开始变小。在

为什么deepcopy()这么慢?在

之所以deepcopy()比大多数其他方法慢得多(感谢@Felix的询问),是因为deepcopy()比简单的列表理解要做更多的工作。与列表理解不同,deecopy()必须在任意嵌套的列表上工作,可能有许多层次的嵌套。因此,在浅层嵌套的列表中使用它是一种极端的过度杀戮,并且会导致执行时间慢得多。在

为了更好地了解deepcopy()在幕后做了什么,您可以查看一下函数的源代码,因为它是open source and available to the public for viewing。在

相关问题 更多 >