Python 深度复制列表列表(速度问题)

Python 深度复制列表列表(速度问题),python,Python,由于表述不清楚,我决定重写我的问题: 我的代码看起来像这样(org应该是一个列表和两个整数的列表): 现在我可以在不影响其他副本的情况下更改b的元素。不像我用的那样 b.append(copy.copy(a)) 循环中 我这样做是为了避免使用copy.deepcopy(),因为它看起来相当慢。现在有三个问题:这段代码是否生成我的列表的深度副本?如果没有,为什么它仍然像b.append(a)那样创建副本而不仅仅是新的引用?此外:我如何才能以更优雅、快速和通灵的方式完成这项工作?对于浅拷贝和深拷贝之

由于表述不清楚,我决定重写我的问题: 我的代码看起来像这样(org应该是一个列表和两个整数的列表):

现在我可以在不影响其他副本的情况下更改b的元素。不像我用的那样

b.append(copy.copy(a))
循环中


我这样做是为了避免使用copy.deepcopy(),因为它看起来相当慢。现在有三个问题:这段代码是否生成我的列表的深度副本?如果没有,为什么它仍然像
b.append(a)
那样创建副本而不仅仅是新的引用?此外:我如何才能以更优雅、快速和通灵的方式完成这项工作?

对于浅拷贝和深拷贝之间的区别,这里似乎存在一些误解。您在问题中声明您正在附加列表列表。让我们假设以下是这样一个列表:

In [32]: x = [[1,2,3],[4,5,6]]
在浅层复制中,我们只复制第一层。从文档中:

浅复制构造一个新的复合对象,然后(尽可能)向其中插入对原始对象的引用

如果我们现在修改
z
的内容,我们将更改
x
,因为我们使用了浅拷贝

In [38]: z[0][0][0]=7

In [39]: x
Out[39]: [[7, 2, 3], [4, 5, 6]]
在深度复制中,我们在所有级别复制对象,实质上创建原始对象的克隆。从文档中:

深度副本构造一个新的复合对象,然后递归地将在原始副本中找到的对象的副本插入其中

Numpy可能是解决这个问题的最快的解决方案,但是您必须重构代码才能获得好处。如果在循环的基本级别中在列表和数组之间进行转换,Numpy将不会对您有所帮助。相反,您应该尽早尝试将问题矢量化,并尽量减少类型转换的次数

编辑:

看看更新问题,似乎有一个非常简单的解决方案。如果列表中的列表仅包含不可变类型,则可以使用以下任一选项:

def my_copy_1(org):
    return (copy.copy(org[0]),org[1],org[2])

def my_copy_2(org):
    return (org[0][:],org[1],org[2])
根据您最初的实现测试这些设备的速度,我得到:

In [2]: a = [[1,2,3],1,2]

In [3]: %timeit tmp.my_copy_orig(a)
100000 loops, best of 3: 2.05 µs per loop

In [4]: %timeit tmp.my_copy_1(a)
100000 loops, best of 3: 2.06 µs per loop

In [5]: %timeit tmp.my_copy_2(a)
1000000 loops, best of 3: 784 ns per loop
就速度而言,
my_copy\u 2
显然是这里的赢家。您可以通过以下方式测试它是否产生正确的行为:

In [6]: a = [[1,2,3],1,2]

In [7]: z = tmp.my_copy_2(a)

In [8]: z[2] = 999

In [9]: z[0][0] = 999

In [10]: a
Out[10]: [[1, 2, 3], 1, 2]

In [11]: z
Out[11]: [[999, 2, 3], 1, 999]

这里似乎对浅拷贝和深拷贝之间的区别有一些误解。您在问题中声明您正在附加列表列表。让我们假设以下是这样一个列表:

In [32]: x = [[1,2,3],[4,5,6]]
在浅层复制中,我们只复制第一层。从文档中:

浅复制构造一个新的复合对象,然后(尽可能)向其中插入对原始对象的引用

如果我们现在修改
z
的内容,我们将更改
x
,因为我们使用了浅拷贝

In [38]: z[0][0][0]=7

In [39]: x
Out[39]: [[7, 2, 3], [4, 5, 6]]
在深度复制中,我们在所有级别复制对象,实质上创建原始对象的克隆。从文档中:

深度副本构造一个新的复合对象,然后递归地将在原始副本中找到的对象的副本插入其中

Numpy可能是解决这个问题的最快的解决方案,但是您必须重构代码才能获得好处。如果在循环的基本级别中在列表和数组之间进行转换,Numpy将不会对您有所帮助。相反,您应该尽早尝试将问题矢量化,并尽量减少类型转换的次数

编辑:

看看更新问题,似乎有一个非常简单的解决方案。如果列表中的列表仅包含不可变类型,则可以使用以下任一选项:

def my_copy_1(org):
    return (copy.copy(org[0]),org[1],org[2])

def my_copy_2(org):
    return (org[0][:],org[1],org[2])
根据您最初的实现测试这些设备的速度,我得到:

In [2]: a = [[1,2,3],1,2]

In [3]: %timeit tmp.my_copy_orig(a)
100000 loops, best of 3: 2.05 µs per loop

In [4]: %timeit tmp.my_copy_1(a)
100000 loops, best of 3: 2.06 µs per loop

In [5]: %timeit tmp.my_copy_2(a)
1000000 loops, best of 3: 784 ns per loop
就速度而言,
my_copy\u 2
显然是这里的赢家。您可以通过以下方式测试它是否产生正确的行为:

In [6]: a = [[1,2,3],1,2]

In [7]: z = tmp.my_copy_2(a)

In [8]: z[2] = 999

In [9]: z[0][0] = 999

In [10]: a
Out[10]: [[1, 2, 3], 1, 2]

In [11]: z
Out[11]: [[999, 2, 3], 1, 999]

如果你问如何制作列表的浅层副本
a
,答案是
a[:]
。这就是你要问的吗?不,我需要一个深度副本:/
list(tuple(list(a))
不是深度副本,如果你的东西只是一个整数列表,你也不需要深度副本。(或者更确切地说,在这种情况下,浅拷贝和深拷贝之间没有区别。)嗯,我想我把浅拷贝和a=b混淆了,a=b只是在同一事物上附加了另一个标记,如果你是这么说的话…
a=b
根本不是拷贝。浅层复制只复制最外层:也就是说,它复制
A
,但不复制
A
包含的内容。深度副本复制
A
包含的所有内容,以及这些内容包含的所有内容,等等。只有当容器中有可变对象(例如列表列表)时,才需要深度副本。如果您询问如何制作列表
A
的浅层副本,答案是
A[:]
。这就是你要问的吗?不,我需要一个深度副本:/
list(tuple(list(a))
不是深度副本,如果你的东西只是一个整数列表,你也不需要深度副本。(或者更确切地说,在这种情况下,浅拷贝和深拷贝之间没有区别。)嗯,我想我把浅拷贝和a=b混淆了,a=b只是在同一事物上附加了另一个标记,如果你是这么说的话…
a=b
根本不是拷贝。浅层复制只复制最外层:也就是说,它复制
A
,但不复制
A
包含的内容。深度副本复制
A
包含的所有内容,以及这些内容包含的所有内容,等等。只有当容器中有可变对象(例如列表列表)时,才需要深度副本。