为什么整数列表的deepcopy返回内存中的相同整数?

2024-10-03 13:19:45 发布

您现在位置:Python中文网/ 问答频道 /正文

正如我在课堂上所学到的,我理解浅拷贝和深拷贝的区别。然而,以下内容没有意义

import copy

a = [1, 2, 3, 4, 5] 

b = copy.deepcopy(a)

print(a is b)
print(a[0] is b[0])
----------------------------
~Output~
>False
>True
----------------------------

当对象及其组成元素在深度副本的不同内存位置重新创建时,print(a[0] is b[0])不应该计算为False吗?我只是在测试这个,因为我们在课堂上讨论过这个,但它似乎不起作用。


Tags: 对象importfalsetrue元素outputis意义
3条回答

如果我们把这看作是一个机械问题,即deepcopy函数调用如何结束返回对相同int对象的引用,而不是它们的副本,那么奥利维尔·梅隆的答案是正确的。我将后退一步,回答为什么这是deepcopy要做的明智之举的问题。

我们需要复制数据结构(深拷贝或浅拷贝)的原因是,我们可以在不影响原始状态的情况下修改它们的内容;或者,我们可以在保留旧状态副本的同时修改原始。当一个数据结构有自己可变的嵌套部分时,就需要一个深度拷贝。考虑这个例子,它将二维网格中的每个数字相乘,比如[[1, 2], [3, 4]]

import copy

def multiply_grid(grid, k):
    new_grid = copy.deepcopy(grid)

    for row in new_grid:
        for i in range(len(row)):
            row[i] *= k

    return new_grid

列表之类的对象是可变的,因此操作row[i] *= k会更改它们的状态。复制列表是防止变异的一种方法;这里需要一个深度副本来复制外部列表和内部列表(即行),它们也是可变的。

但整数和字符串等对象是不可变的,因此不能修改它们的状态。如果一个int对象是13,那么它将保持13,即使您将它乘以k;乘法将产生一个不同的int对象。没有变异可以防御,因此不需要复制。

有趣的是,deepcopy如果元组的组件都是不可变的,则不会复制元组,但如果元组的组件是可变的,则会复制元组:

>>> import copy
>>> x = ([1, 2], [3, 4])
>>> x is copy.deepcopy(x)
False
>>> y = (1, 2)
>>> y is copy.deepcopy(y)
True

其逻辑是相同的:如果一个对象是不可变的,但是有可变的嵌套组件,那么需要一个副本来避免对原始组件的变异。但是,如果整个结构是完全不可变的,就没有可以防御的突变,因此也就不需要拷贝。

在另一个答案中,有人认为这可能是因为Python为小整数保留了对象。虽然这种说法是正确的,但并不是导致这种行为的原因。

让我们看看当我们使用更大的整数时会发生什么。

> from copy import deepcopy
> x = 1000
> x is deepcopy(x)
True

如果深入研究copy模块,我们会发现使用原子值调用deepcopy会延迟对函数^{}的调用。

def _deepcopy_atomic(x, memo):
    return x

所以实际发生的情况是deepcopy不会复制原子值,而只返回它。

举个例子,这就是intfloatstrfunction等等的情况。

这种行为的原因是Python优化小整数,使它们实际上不在不同的内存位置。查看1id,它们总是相同的:

>>> x = 1
>>> y = 1
>>> id(x)
1353557072
>>> id(y)
1353557072

>>> a = [1, 2, 3, 4, 5]
>>> id(a[0])
1353557072

>>> import copy
>>> b = copy.deepcopy(a)
>>> id(b[0])
1353557072

来自Integer Objects的引用:

The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :-)

相关问题 更多 >