Skip to content

拷贝模块:理解 Python 中的对象复制

在 Python 编程中,我们经常需要复制对象。然而,简单的赋值操作并不能创建对象的独立副本,而是创建了一个指向相同内存地址的引用。copy 模块提供了 copy()(浅拷贝)和 deepcopy()(深拷贝)方法,用于创建对象的副本,从而避免修改一个对象时影响到其他对象。

浅拷贝的局限性:引用与共享

copy.copy() 方法执行的是浅拷贝。这意味着它会创建一个新的对象,但新对象中的子对象仍然是原始对象的引用。换句话说,浅拷贝只复制了对象的顶层结构,而子对象仍然共享相同的内存地址。

让我们通过一个例子来说明浅拷贝的局限性:

python
>>> old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 使用 copy() 方法进行浅拷贝
>>> new_list = old_list.copy()

# 检查两个列表是否指向同一个对象(False,列表本身是新的)
>>> id(new_list) == id(old_list)
False

# 检查两个列表的项是否指向相同的对象(True,子列表是共享的)
>>> [id(new_list[idx]) == id(old_list[idx]) for idx in range(len(old_list))]
[True, True, True]

# 修改新列表的子列表
>>> new_list[1][1] = 0
>>> new_list
[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
>>> old_list  # old_list 也被修改了
[[1, 2, 3], [4, 0, 6], [7, 8, 9]]

在这个例子中,我们创建了一个嵌套列表 old_list,然后使用 copy() 方法创建了 new_list。可以看到,new_list 本身是一个新的对象,但它的子列表仍然指向与 old_list 相同的内存地址。因此,当我们修改 new_list 的子列表时,old_list 也会受到影响。

简单来说,浅拷贝复制了容器对象本身,但是容器内的元素仍然是原有对象的引用。

深拷贝:创建完全独立的副本

为了解决浅拷贝的局限性,copy 模块提供了 deepcopy() 方法。深拷贝会递归地复制对象及其所有子对象,从而创建一个完全独立的副本。这意味着修改新对象不会影响原始对象。

python
>>> import copy
>>> old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 使用 copy.deepcopy() 方法进行深拷贝
>>> new_list = copy.deepcopy(old_list)

# 检查两个列表是否指向同一个对象(False)
>>> id(new_list) == id(old_list)
False

# 检查两个列表的项是否指向相同的对象(False,子列表也是新的)
>>> [id(new_list[idx]) == id(old_list[idx]) for idx in range(len(old_list))]
[False, False, False]

# 修改新列表的子列表
>>> new_list[1][1] = 0
>>> new_list
[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
>>> old_list  # old_list 未被修改
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

在这个例子中,我们使用 copy.deepcopy() 创建了 new_list。现在,new_list 及其所有子列表都是 old_list 的独立副本。因此,当我们修改 new_list 的子列表时,old_list 不会受到影响。

选择合适的拷贝方式

  • 当你只需要复制对象的顶层结构,而不需要复制子对象时,可以使用 copy() 方法进行浅拷贝。
  • 当你需要创建对象的完全独立副本,包括所有子对象时,可以使用 copy.deepcopy() 方法进行深拷贝。

选择哪种拷贝方式取决于你的具体需求。如果你不确定,最好使用深拷贝,以避免潜在的副作用。

额外补充:

  • 可变对象与不可变对象:理解可变对象(例如列表、字典)和不可变对象(例如数字、字符串、元组)的区别对于理解拷贝行为至关重要。 深拷贝会递归复制所有对象,而浅拷贝对于不可变对象,实际上复制的也是引用,但是由于其不可变性,修改操作会创建新的对象,所以看起来和深拷贝效果类似。
  • 自定义对象的拷贝:你可以通过在自定义类中实现 __copy__()__deepcopy__() 方法来控制对象的拷贝行为。

掌握拷贝模块是编写健壮、可维护的 Python 代码的关键。通过理解浅拷贝和深拷贝的区别,你可以避免许多潜在的 bug,并确保你的程序能够正确地处理对象复制。