跳到主要内容

Python 浅拷贝和深拷贝

提示
  1. Python中对象的复制:在Python中,使用=运算符复制对象不会创建新对象,而是创建了一个新变量,共享原始对象的引用。
  2. 浅拷贝和深拷贝:Python的copy模块可以进行浅拷贝(copy.copy())和深拷贝(copy.deepcopy())。浅拷贝创建新对象,但不复制嵌套对象,而深拷贝创建原始对象及其所有嵌套对象的独立副本。
  3. 浅拷贝和深拷贝的区别:浅拷贝的修改会影响原始对象的嵌套对象,而深拷贝的修改则不会影响原始对象。

在 Python 中复制对象

在 Python 中,我们使用 = 运算符创建对象的副本。你可能会认为这会创建一个新对象;实际上并不会。它只是创建了一个新变量,该变量共享原始对象的引用。

让我们看一个示例,在这个示例中,我们创建了一个名为 old_list 的列表,并使用 = 运算符将对象引用传递给 new_list。

示例 1:使用 = 运算符复制

old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 'a']]
new_list = old_list

new_list[2][2] = 9

print('旧列表:', old_list)
print('旧列表的 ID:', id(old_list))

print('新列表:', new_list)
print('新列表的 ID:', id(new_list))

当我们运行上述程序时,输出将是:

旧列表: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
旧列表的 ID: 140673303268168

新列表: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
新列表的 ID: 140673303268168

如你所见,old_list 和 new_list 两个变量共享相同的 id,即 140673303268168

因此,如果你想修改 new_list 或 old_list 中的任何值,更改将在两者中都可见。

本质上,有时你可能希望保持原始值不变,只修改新值,反之亦然。在 Python 中,有两种创建副本的方式:

  1. 浅拷贝
  2. 深拷贝

为了使这些拷贝工作,我们使用 copy 模块。

复制模块

我们使用 Python 的 copy 模块进行浅拷贝和深拷贝操作。假设,你需要复制一个复合列表,比如 x。例如:

import copy
copy.copy(x)
copy.deepcopy(x)

这里,copy() 返回 x 的浅拷贝。同样地,deepcopy() 返回 x 的深拷贝。

浅拷贝

浅拷贝创建了一个新对象,该对象存储原始元素的引用。

因此,浅拷贝不会创建嵌套对象的副本,而只是复制嵌套对象的引用。这意味着,复制过程不会递归或自身创建嵌套对象的副本。

示例 2:使用浅拷贝创建副本

import copy

old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
new_list = copy.copy(old_list)

print("旧列表:", old_list)
print("新列表:", new_list)

当我们运行程序时,输出将是:

旧列表: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
新列表: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

在上述程序中,我们创建了一个嵌套列表,然后使用 copy() 方法进行了浅拷贝。

这意味着它会创建具有相同内容的新对象和独立对象。为了验证这一点,我们打印了 old_list 和 new_list。

为了确认 new_list 与 old_list 不同,我们尝试向原始列表中添加新的嵌套对象,并检查它。

示例 3:向 old_list 添加 [4, 4, 4],使用浅拷贝

import copy

old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.copy(old_list)

old_list.append([4, 4, 4])

print("Old list:", old_list)
print("New list:", new_list)

当我们运行程序时,它将输出:

Old list: [[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]

在上述程序中,我们创建了 old_list 的浅拷贝。new_list 包含了存储在 old_list 中的原始嵌套对象的引用。然后我们向 old_list 中添加了新的列表 [4, 4, 4]。这个新的子列表没有在 new_list 中被拷贝。

然而,当你改变 old_list 中的任何嵌套对象时,这些变化会在 new_list 中出现。

示例 4:使用浅拷贝添加新的嵌套对象

import copy

old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.copy(old_list)

old_list[1][1] = 'AA'

print("Old list:", old_list)
print("New list:", new_list)

当我们运行程序时,它将输出:

Old list: [[1, 1, 1], [2, 'AA', 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 'AA', 2], [3, 3, 3]]

在上述程序中,我们修改了 old_list,即 old_list[1][1] = 'AA'。old_list 和 new_list 的子列表在索引 [1][1] 的位置都被修改了。这是因为两个列表共享了同一个嵌套对象的引用。

深拷贝

深拷贝创建一个新对象,并递归地添加原始元素中存在的嵌套对象的副本。

让我们继续第二个示例。然而,我们将使用 copy 模块中的 deepcopy() 函数创建深拷贝。深拷贝会创建原始对象及其所有嵌套对象的独立副本。

示例 5:使用 deepcopy() 拷贝列表

import copy

old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.deepcopy(old_list)

print("Old list:", old_list)
print("New list:", new_list)

当我们运行程序时,它将输出:

Old list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]

在上述程序中,我们使用 deepcopy() 函数创建了看起来相似的副本。

然而,如果你对原始对象 old_list 中的任何嵌套对象进行更改,你会发现新的副本 new_list 没有发生任何改变。

示例 6:使用深拷贝在列表中添加新的嵌套对象

import copy

old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.deepcopy(old_list)

old_list[1][0] = 'BB'

print("Old list:", old_list)
print("New list:", new_list)

当我们运行程序时,它将输出:

Old list: [[1, 1, 1], ['BB',

2, 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]

在上述程序中,当我们为 old_list 赋予一个新值时,我们可以看到只有 old_list 被修改了。这意味着 old_list 和 new_list 是独立的。这是因为 old_list 被递归地拷贝了,这同样适用于其所有的嵌套对象。