8. 序列操作进阶
8.3 序列复制

基本概念

在实际编程开发中,我们经常需要给一个序列对象赋值。前面介绍了很多直接赋值的操作,例如:name_list = ['Tom', 'Jerry'] 或者 fruits = ('apple', 'orange', 'banana') 等;但是,如果我们想要将一个序列对象的值复制给另一个序列对象,我们该怎么操作呢?

在本节中,我们就来详细地讲解一下如何在 Python 中对序列进行复制操作。

基本用法

在 Python 中,我们可以使用以下几种不同的方法来复制列表或元组:

方法 1:使用切片操作

我们可以使用切片来复制列表或元组,如下所示:

# 复制列表
original_list = [1, 2, 3, 4, 5]
new_list = original_list[:]
print(new_list)   # 输出:[1, 2, 3, 4, 5]
 
# 复制元组
original_tuple = (1, 2, 3, 4, 5)
new_tuple = original_tuple[:]
print(new_tuple)  # 输出:(1, 2, 3, 4, 5)
    ```
 
### 方法 2:使用 `list()` 或 `tuple()` 函数
 
我们可以使用 Python 内置的 `list()` 或 `tuple()` 函数来复制列表或元组,如下所示:
 
```python
# 复制列表
original_list = [1, 2, 3, 4, 5]
new_list = list(original_list)
print(new_list)   # 输出:[1, 2, 3, 4, 5]
 
# 复制元组
original_tuple = (1, 2, 3, 4, 5)
new_tuple = tuple(original_tuple)
print(new_tuple)  # 输出:(1, 2, 3, 4, 5)

方法 3:使用 copy() 方法

我们可以使用 Python 提供的 copy() 方法来复制列表,如下所示:

# 复制列表
original_list = [1, 2, 3, 4, 5]
new_list = original_list.copy()
print(new_list)   # 输出:[1, 2, 3, 4, 5]

需要注意的是,copy() 方法仅适用于列表,而不适用于元组;也就是说,我们无法使用 copy() 方法来复制一个元组。

另外,我们必须要牢记:上面列出的三种方法所提供的复制操作都是一种浅复制,它并不会完整原始对象的全部内容;如果我们需要复制原始对象的全部内容,我们需要使用深复制方法 deepcopy()

下面就让我们就详细讲解一下 Python 中与复制拷贝相关的两个概念:浅复制与深复制。

浅复制与深复制

当我们在代码中想要复制一个对象时,Python 为我们提供了两种模式:浅复制深复制。它们的主要区别如下所示:

浅复制

在 Python 中,浅复制创建一个新对象,但不是复制原始对象的整个内容,而是只创建对原始内存位置的引用。换句话说,新对象指向与原始对象相同的内存位置。

以下是创建列表的浅复制的示例:

original_list = [1, 2, [3, 4], 5]
shallow_copy_1 = original_list.copy()
shallow_copy_2 = list(original_list)
shallow_copy_3 = original_list[:]
 
# 更改原始列表中的可变对象
original_list[2][0] = 9
 
print(original_list)    # 输出:[1, 2, [9, 4], 5]
print(shallow_copy_1)      # 输出:[1, 2, [9, 4], 5]
print(shallow_copy_2)      # 输出:[1, 2, [9, 4], 5]
print(shallow_copy_3)      # 输出:[1, 2, [9, 4], 5]

在上面的示例中,original_list 包含一个嵌套列表,即 [3, 4]。当我们使用前面介绍的三种方法对 original_list 进行复制时,新列表只包含了对该嵌套列表在同一内存中的引用,是一种浅复制。因此,当我们修改原始列表对象 original_list 中的嵌套列表时,所有的新列表 shallow_copy_1shallow_copy_2shallow_copy_3 都会受到影响,因为它们都是引用了嵌套列表的相同内存位置。

深复制

与浅复制相反,深复制创建一个全新的对象,包括其所有内容。换句话说,它还会递归地复制原始对象中的所有嵌套对象,而不仅仅是创建对它们的引用。

以下是深复制一个列表的示例:

import copy
 
original_list = [1, 2, [3, 4], 5]
deep_copy = copy.deepcopy(original_list)
 
# 更改原始列表中的可变对象
original_list[2][0] = 9
 
print(original_list)    # 输出:[1, 2, [9, 4], 5]
print(deep_copy)         # 输出:[1, 2, [3, 4], 5]

在上面的示例中,我们使用 copy.deepcopy() 方法创建 original_list 的深度复制。这将在内存中创建一个全新的对象,包括其所有嵌套对象。因此,当我们修改 original_list 中的嵌套列表时,它不会影响到 deep_copy 中的嵌套列表,因为它们是内存中的不同对象。

在上面的示例中使用到的 copy()deepcopy() 方法都是 Python 中的内置函数,本质上,它们都是 copy 模块的一部分。原则上,在使用这些函数之前,我们需要先导入 copy 模块。例如:

import copy
 
original_list = [1, 2, [3, 4], 5]
 
# 使用 copy() 复制列表并得到浅复制
shallow_copy = copy.copy(original_list)
 
# 使用 deepcopy() 复制列表并得到深复制
deep_copy = copy.deepcopy(original_list)

但是,如果我们只需要复制一个对象的浅层副本,那么,我们可以直接使用内置的 copy 模块中的 copy() 函数而无需显式导入该模块;相反,如果我们想复制一个对象的深层副本,那么,我们必须显式导入 copy 模块,然后调用其中的 deepcopy() 函数。例如:

original_list = [1, 2, [3, 4], 5]
 
# 使用内置的 copy() 函数复制列表并得到浅复制
shallow_copy = copy.copy(original_list)
 
# 这会引发错误,因为 deep_copy() 函数尚未定义
deep_copy = deepcopy(original_list)
# NameError: name 'deepcopy' is not defined
 
# 要使用 deepcopy(),您需要显式导入 copy 模块
import copy
deep_copy = copy.deepcopy(original_list)

在上面的示例中,我们首先创建一个原始列表,并使用内置的 copy 模块中的 copy() 函数复制该列表以获得浅复制,而不必显式导入该模块。但是,当我们尝试直接使用 deepcopy() 函数而不导入 copy 模块时,它会引发 NameError。要使用 deepcopy(),我们必须显式地导入 copy 模块。

总结

综上所述,在 Python 中,我们通常有三种方法对序列对象进行复制操作:

  • 使用切片
  • 使用 list()tuple() 函数
  • 使用 copy() 方法

对于复制操作而言,Python 为我们提供了两种模式:浅复制和深复制。上述三种方法所实现的都是浅复制。如果我们想复制一个对象的深层副本,那么,我们必须导入 copy 模块,然后调用其中的 deepcopy() 函数。