Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 对于相同大小的列表,为什么深度副本比浅层副本慢得多?_Python_Python 3.x_Copy_Deep Copy - Fatal编程技术网

Python 对于相同大小的列表,为什么深度副本比浅层副本慢得多?

Python 对于相同大小的列表,为什么深度副本比浅层副本慢得多?,python,python-3.x,copy,deep-copy,Python,Python 3.x,Copy,Deep Copy,我一直在开发一个性能关键型应用程序,它经常需要复制一个二维整数列表,并修改副本。我正在实现minimax算法 我注意到,在相同元素数的列表上,副本和深度副本在性能上存在巨大差异,我想了解我的想法是否正确 要重现我的问题,请运行以下代码: import numpy as np np.random.seed(0) lst1 = np.random.randint(100, size=1000 * 1000).tolist() lst2 = np.random.randint(100, size=(

我一直在开发一个性能关键型应用程序,它经常需要复制一个二维整数列表,并修改副本。我正在实现minimax算法

我注意到,在相同元素数的列表上,副本和深度副本在性能上存在巨大差异,我想了解我的想法是否正确

要重现我的问题,请运行以下代码:

import numpy as np

np.random.seed(0)
lst1 = np.random.randint(100, size=1000 * 1000).tolist()
lst2 = np.random.randint(100, size=(1000, 1000)).tolist()
现在,为下面的语句计时,您应该会看到与我类似的计时

%timeit copy.copy(lst1)
%timeit lst1.copy()
%timeit copy.deepcopy(lst2)

5 ms ± 49.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.47 ms ± 551 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.61 s ± 112 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
lst1和lst2都有一百万个元素,但可靠地复制前者比相同元素数的嵌套列表快200倍。我认为这与这样一个事实有关,即制作嵌套列表的深度副本可能需要一些递归实现,这很慢,所以我尝试了

%timeit copy.deepcopy(lst1)
1.43 s ± 90.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 
而时间安排仍显示出大幅放缓。我查过了,但没有多少解释。然而,从计时来看,我怀疑deepcopy也在复制每个int,从而创建新的整数。但这似乎是一件浪费的事情

我的想法正确吗?“深度复制”在这里做什么?复制和“浅层复制”不在这里吗

我看到了,但这个问题似乎是在寻求一个替代方案,而不是一个我不清楚的解释

deepcopy没有复制int。无论如何,它不可能做到这一点

deepcopy的速度很慢,因为它需要处理深度拷贝的全部复杂性,即使这被证明是不必要的。这包括为找到的每一个对象分配到相应的复印机,即使复印机最终被发现。这包括维护备忘记录和跟踪复制的每个对象,以处理对相同对象的重复引用,即使没有。这包括对数据结构(如和)的特殊复制处理,因此在尝试使用递归引用复制数据结构时,不会进入无限递归

所有这一切都必须完成,不管它是否有回报。所有这些都很昂贵

此外,deepcopy是纯Python。那没用。将deepcopy与pickle.loadspickle.dumpswhatever(执行非常类似的工作)进行比较,pickle凭借C实现轻松获胜。在Python2上,将pickle替换为cPickle。pickle仍然很难被利用已知输入结构的实现所取代,不过:

In [15]: x = [[0]*1000 for i in range(1000)]

In [16]: %timeit copy.deepcopy(x)
1.05 s ± 5.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [17]: %timeit pickle.loads(pickle.dumps(x))
78 ms ± 4.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [18]: %timeit [l[:] for l in x]
4.56 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
deepcopy没有复制Int。无论如何,它不可能做到这一点

deepcopy的速度很慢,因为它需要处理深度拷贝的全部复杂性,即使这被证明是不必要的。这包括为找到的每一个对象分配到相应的复印机,即使复印机最终被发现。这包括维护备忘记录和跟踪复制的每个对象,以处理对相同对象的重复引用,即使没有。这包括对数据结构(如和)的特殊复制处理,因此在尝试使用递归引用复制数据结构时,不会进入无限递归

所有这一切都必须完成,不管它是否有回报。所有这些都很昂贵

此外,deepcopy是纯Python。那没用。将deepcopy与pickle.loadspickle.dumpswhatever(执行非常类似的工作)进行比较,pickle凭借C实现轻松获胜。在Python2上,将pickle替换为cPickle。pickle仍然很难被利用已知输入结构的实现所取代,不过:

In [15]: x = [[0]*1000 for i in range(1000)]

In [16]: %timeit copy.deepcopy(x)
1.05 s ± 5.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [17]: %timeit pickle.loads(pickle.dumps(x))
78 ms ± 4.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [18]: %timeit [l[:] for l in x]
4.56 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

在编程中,深度拷贝相当于某个东西的物理拷贝。它是原始对象的实际副本。在大多数编程工具中,您可以使用它,在不影响原始对象的情况下修改它。
但是,另一方面,浅层副本是对原始对象的引用。如果更改它,也会影响原始对象。 简言之,由于深度副本是原始对象的实际副本,因此仅指向原始对象的浅层副本较重

浅显复制:你可以有一张新家具的照片,了解它到底是什么样子。你可以很容易地随身携带这幅画


深度复制:你可以去家具店,看看真正的家具。您可能无法轻松随身携带,您可能需要一些帮助才能将其带回家。

在编程中,深度复制相当于某个内容的物理拷贝。它是原始对象的实际副本。在大多数编程工具中,您可以使用它,在不影响原始对象的情况下修改它。
但是,另一方面,浅层副本是对原始对象的引用。如果更改它,也会影响原始对象。 简言之,由于深度副本是原始对象的实际副本,因此仅指向原始对象的浅层副本较重

浅拷贝:你可以有一个pictu 重新审视你的新家具,了解它到底是什么样子。你可以很容易地随身携带这幅画

深度复制:你可以去家具店,看看真正的家具。你可能不能轻易地随身携带,你可能需要一些帮助才能把它带回家

浅复制和深复制之间的区别仅与包含其他对象(如列表或类实例)的复合对象相关:

浅复制构造一个新的复合对象,然后尽可能在其中插入对原始对象的引用。 深度副本构造一个新的复合对象,然后递归地将在原始副本中找到的对象的副本插入其中 因此,浅层副本将创建一个新列表,并使用对原始列表中每个元素的引用填充该列表。因为原始列表中的每个元素本身就是一个列表,所以只存储对它的引用要比创建新副本快得多。 Deepcopy在如何复制每个元素以避免错误方面做了一些巧妙的工作。但本质上,你不需要了解为什么一个浅拷贝比深度拷贝快

浅复制和深复制之间的区别仅与包含其他对象(如列表或类实例)的复合对象相关:

浅复制构造一个新的复合对象,然后尽可能在其中插入对原始对象的引用。 深度副本构造一个新的复合对象,然后递归地将在原始副本中找到的对象的副本插入其中 因此,浅层副本将创建一个新列表,并使用对原始列表中每个元素的引用填充该列表。因为原始列表中的每个元素本身就是一个列表,所以只存储对它的引用要比创建新副本快得多。
Deepcopy在如何复制每个元素以避免错误方面做了一些巧妙的工作。但本质上,你不需要了解为什么一个浅拷贝比deepcopy快……

这可能确实是浪费,但deepcopy就是这么做的:它复制所有东西。它不知道你只想复制列表。它不会复制所有内容,也不会复制不可变的内置类型,但它会检查所有内容,并维护所有可见对象的缓存。谢谢大家的评论。请考虑把它们作为答案。似乎我唯一的选择是切换到1D实现,并实现一些逻辑,以便在访问代码中的列表元素时将2D索引转换为1D索引。请注意,以下是源代码:如果您确切知道列表的形状,您可能可以实现自己的简单复制操作,即:[sub.copy用于嵌套_列表中的sub]这将是非常浪费的,但这确实是浪费,但它是复制所有的东西。它不知道你只想复制列表。它不复制任何不复制不可改变的内置类型的东西,但是它检查所有的东西并保存所有看到的对象的缓存。将它们作为答案。我唯一的选择似乎是切换到1D实现,并在访问代码中的列表元素时实现一些逻辑,将2D索引转换为1D索引。请注意,以下是源代码:如果您确切知道列表的形状,您可能可以实现自己的原始复制操作,即:[sub.copy for sub in nested_list]。这会快得多。但是,另一方面,浅层副本是对原始对象的引用。如果更改它,它也会影响原始对象。这不是真的。a=[];导入副本;b=copy.copya;a.append'foo';print'b:',b,'a:',a.然而,另一方面,浅层副本是对原始对象的引用。如果更改它,它也会影响原始对象。这不是真的。a=[];导入副本;b=copy.copya;a.append'foo';print'b:',b,'a:',a。