copy --- 浅层和深层复制操作

源代码: Lib/copy.py


Python 中的赋值语句不复制对象,而是在目标和对象之间创建绑定。对于可变或包含可变项的集合,有时需要副本,以便可以更改一个副本而无需更改另一个。本模块提供了通用的浅层和深层复制操作(如下所述)。

接口摘要

copy.copy(obj)

返回 obj 的浅层复制。

copy.deepcopy(obj[, memo])

返回 obj 的深层复制。

copy.replace(obj, /, **changes)

创建一个与 obj 类型相同的新对象,并用 changes 中的值替换字段。

在 3.13 版本加入。

exception copy.Error

针对模块特定错误引发。

浅层和深层复制之间的区别仅与复合对象(包含其他对象,如列表或类实例的对象)有关:

  • 浅层复制 会创建一个新的复合对象,然后(在可能的范围内)将原始对象中元素的 引用 插入其中。

  • 深层复制 会创建一个新的复合对象,然后递归地将原始对象中元素的 副本 插入其中。

深层复制操作通常存在两个浅层复制操作不存在的问题:

  • 递归对象(直接或间接包含对自身引用的复合对象)可能会导致递归循环。

  • 因为深层复制会复制所有内容,所以它可能会复制过多的数据,例如本应在副本之间共享的数据。

deepcopy() 函数通过以下方式避免了这些问题:

  • 保留一个 memo 字典,用于记录在当前复制过程中已复制的对象;以及

  • 让用户自定义的类可以重写复制操作或要复制的组件集。

此模块不复制模块、方法、栈跟踪、栈帧、文件、套接字、窗口或任何类似类型。它确实“复制”了函数和类(浅层和深层),通过返回原始对象而不做改变;这与 pickle 模块处理这些类型的方式是兼容的。

字典的浅层复制可以使用 dict.copy() 来创建,列表的浅层复制可以通过对整个列表进行切片赋值来创建,例如 copied_list = original_list[:]

类可以使用与控制 pickling 相同的接口来控制复制。有关这些方法的信息,请参阅 pickle 模块的描述。事实上,copy 模块使用了来自 copyreg 模块中已注册的 pickle 函数。

为了让一个类定义自己的复制实现,它可以定义特殊方法 __copy__()__deepcopy__()

object.__copy__(self)

调用以实现浅层复制操作;不传递额外参数。

object.__deepcopy__(self, memo)

调用以实现深层复制操作;它被传递一个参数,即 memo 字典。如果 __deepcopy__ 的实现需要对一个组件进行深层复制,它应该调用 deepcopy() 函数,并将该组件作为第一个参数,memo 字典作为第二个参数。memo 字典应被视为一个不透明对象。

函数 copy.replace()copy()deepcopy() 功能更受限,只支持由 namedtuple() 创建的命名元组、dataclasses 以及其他定义了 __replace__() 方法的类。

object.__replace__(self, /, **changes)

此方法应创建一个相同类型的新对象,并用 changes 中的值替换字段。

在 3.13 版本加入。

参见

pickle 模块

关于用于支持对象状态获取和恢复的特殊方法的讨论。