Python 浅拷贝和深拷贝

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:', old_list)
print('ID of Old List:', id(old_list))

print('New List:', new_list)
print('ID of New List:', id(new_list))

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

Old List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ID of Old List: 140673303268168

New List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ID of New List: 140673303268168

从输出可以看出,old_listnew_list 这两个变量共享相同的 id,即 140673303268168

因此,如果您想修改 new_listold_list 中的任何值,更改在两者中都会显示。


本质上,有时您可能希望原始值保持不变,而只修改新值,反之亦然。在 Python 中,有两种复制方法:

  1. 浅拷贝
  2. 深拷贝

为了使这些复制生效,我们使用 copy 模块。


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:", old_list)
print("New list:", new_list)

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

Old list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
New list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

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

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

为确认 new_listold_list 不同,我们尝试将新的嵌套对象添加到原始对象中并进行检查。


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

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 中的原始嵌套对象的引用。然后我们将新列表(即 [4, 4, 4])添加到 old_list 中。这个新的子列表没有被复制到 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_listnew_list 在索引 [1][1] 处的两个子列表都被修改了。这是因为两个列表共享相同的嵌套对象的引用。


深拷贝

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

让我们继续示例 2。但是,我们将使用 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 被递归复制了,这对于其所有嵌套对象都适用。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战