如何在Python中深度和浅度复制对象

83 阅读5分钟

简介

在这篇文章中,我们将看看如何在Python中深度浅度复制对象。

简短的回答是,你可以使用copy 模块的方法来进行这两种操作。

import copy

shallow_copy_list = copy.copy(original_list)
deepcopy_list = copy.deepcopy(original_list)

然而,以浅层或深层方式复制一个东西是什么意思?

在接下来的章节中,我们将深入探讨这些术语的含义,Python如何处理内存中的对象引用和对象,以及为什么这两种方法以它们的方式工作。

在 Python 中浅层拷贝一个对象

当我们在 Python 中使用赋值语句 (=) 来创建复合对象的副本时,比如列表或类实例,或者基本上任何包含其他对象的对象,Python 不会克隆对象本身。相反,它只是将引用绑定到目标对象上。

想象一下,我们有一个列表,其中有以下元素。

original_list =[[1,2,3], [4,5,6], ["X", "Y", "Z"]]

如果我们试图使用如下的赋值语句来复制我们的原始列表。

shallow_copy_list = original_list
print(shallow_copy_list)

它可能看起来像我们克隆了我们的对象,现在有两个。

[[1,2,3], [4,5,6], ['X', 'Y', 'Z']]

但是,我们真的有两个对象吗?**不,我们没有。**我们有两个引用变量,指向内存中的同一个对象。通过打印这两个对象在内存中的ID,可以很容易地验证这一点。

id(original_list) # 4517445712
id(shallow_copy_list) # 4517445712

一个更具体的证明可以通过尝试改变 "两个列表 "中的一个值来观察--而实际上,我们改变的是同一个列表,两个指针都指向内存中的同一个对象。

让我们访问由original_list 指向的对象的最后一个元素。

# Last element of last element
original_list[-1][-1] = "ZZZ"
print(original_list)

这就导致了:

[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]

知道两个引用变量都指向同一个对象,打印shallow_copy_list ,将返回相同的结果。

print(shallow_copy_list)
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]

浅层复制是指复制一个对象的引用并将其存储在一个新的变量中的过程。original_listshallow_copy_list 只是指向内存(RAM)中相同地址的引用,这些地址存储了[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']] 的值。

An illustration of the shallow copy

也可以使用整个列表的一个切片和赋值语句来创建一个对象的浅层拷贝。

slice_shallow_copy_list = original_list[:]

另一种浅层拷贝的方法是使用Python标准库的copy 模块。

要使用copy 模块,我们必须首先导入它。

import copy

现在我们可以使用copy 模块的copy() 方法。

second_shallow_copy_list = copy.copy(original_list)

打印它们,看看它们是否引用了相同的值。

print(original_list)
print(second_shallow_copy_list)

正如预期的那样,它们是相同的。

[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]

通常,你想复制一个复合对象,例如在一个方法的开头,然后修改克隆的对象,但保留原来的对象,以便以后再使用它。

为了实现这一点,我们需要 深度拷贝的对象。现在让我们来学习什么是深度拷贝以及如何深度拷贝一个复合对象。

在 Python 中深度拷贝一个对象

深度拷贝一个对象意味着真正地把这个对象和它的值克隆到内存中的一个新的副本 (实例) 中,并具有这些相同的值。

通过深度拷贝,我们实际上可以创建一个独立于原始数据的新对象,而不是为相同的值创建一个新的引用,但包含相同的值。

在一个典型的深度拷贝过程中,首先,一个新的对象引用被创建,然后所有的子对象被递归地添加到父对象中。

这样一来,与浅层拷贝不同,对原始对象的任何修改都不会反映在拷贝对象中(反之亦然)。

下面是一个典型的深度拷贝的简单图示。

An illustration of the deep copy

要在Python中深度拷贝一个对象,我们使用copy 模块的deepcopy() 方法。

让我们导入 copy 模块并创建一个列表的深度拷贝。

import copy
 
original_list = [[1,2,3], [4,5,6], ["X", "Y", "Z"]]
deepcopy_list = copy.deepcopy(original_list)

现在让我们打印我们的列表,以确保输出是相同的,还有它们的 ID 作为它们唯一性的证明。

print(id(original_list), original_list)
print(id(deepcopy_list), deepcopy_list)

输出结果证实了我们已经为自己创建了一个真正的副本。

4517599280, [[1, 2, 3], [4, 5, 6], ['X', 'Y', 'Z']]
4517599424, [[1, 2, 3], [4, 5, 6], ['X', 'Y', 'Z']]

现在让我们试着修改我们的原始列表,将最后一个列表的最后一个元素改为 "O",然后打印出来看看结果。

original_list[-1][-1] = "O"
print(original_list)

我们得到的结果和预期的一样。

[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'O']]

现在,如果我们继续尝试打印我们的副本列表。

print(deepcopy_list)

之前的修改并没有反映在这个列表上。

[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'Z']]

记住,copy()deepcopy() 方法适用于其他复合对象。这意味着你也可以用它们来创建类实例的副本。

总结

在这篇文章中,我们学习了浅层拷贝和深层拷贝对象的含义。

我们还了解到,我们可以使用copy 模块的copy() 方法来创建浅层拷贝,而使用deepcopy() 方法来创建复合对象的深层拷贝。