源代码: Lib/copy.py
    严格来说,Python 中赋值语句并不是把对象的值赋给变量名,而是在变量名和对象之间创建绑定关系,或者说给存储在数据起一个名字或加一个标签以方便重复访问。
    对于自身可变(如列表)或者包含可变项(如列表做为元素)的集合对象,在程序开发时有时会需要生成其副本用于改变操作,避免改变原对象。copy 模块提供了通用的浅层 (shallow) 复制和深层 (deep)复制操作,语法如下:

    1. copy.copy(x) # 返回 x 的浅层复制。
    2. copy.deepcopy(x[, memo]) # 返回 x 的深层复制。
    3. exception copy.error # 针对模块特定错误引发。

    浅层复制和深层复制都会创新一个新的对象,他们之间的区别仅与复合对象 (即包含其他对象的对象,如列表或类的实例) 相关:

    • 一个 浅层复制 会构造一个新的复合对象,然后(在可能的范围内)将原对象中找到的 引用 插入其中。
    • 一个 深层复制 会构造一个新的复合对象,然后递归地将原始对象中所找到的对象的 副本 插入。
    1. import copy
    2. ls = [1,2,'123',[4,5,(7,8,[9,10])]]
    3. ls2 = copy.copy(ls)
    4. ls3 = copy.deepcopy(ls)

    copy.png
    深度复制操作通常存在两个问题, 而浅层复制操作并不存在这些问题:

    • 递归对象 (直接或间接包含对自身引用的复合对象) 可能会导致递归循环。
    • 由于深层复制会复制所有内容,因此可能会过多复制(例如本应该在副本之间共享的数据)。

    deepcopy() 函数通过以下操作避免这些问题:

    • 保留在当前复制过程中已复制的对象的 “备忘录” (memo) 字典;
    • 允许用户定义的类重载复制操作或复制的组件集合。

    该模块不复制模块、方法、栈追踪(stack trace)、栈帧(stack frame)、文件、套接字、窗口、数组以及任何类似的类型。它通过不改变地返回原始对象来(浅层或深层地)“复制”函数和类;这与 pickle 模块处理这类问题的方式是相似的。
    制作字典的浅层复制可以使用 dict.copy() 方法,而制作列表的浅层复制可以通过赋值整个列表的切片完成,例如:

    1. copied_list = original_list[:]

    类可以使用与控制序列化(pickling)操作相同的接口来控制复制操作,关于这些方法的描述信息可参考 pickle模块。实际上,copy 模块使用的正是从 copyreg 模块中注册的 pickle 函数。
    想要给一个类定义它自己的拷贝操作实现,可以通过定义特殊方法 __copy__()__deepcopy__()。 调用前者以实现浅层拷贝操作,该方法不用传入额外参数。 调用后者以实现深层拷贝操作;它应传入一个参数即 memo字典。 如果 __deepcopy__() 实现需要创建一个组件的深层拷贝,它应当调用 deepcopy() 函数并以该组件作为第一个参数,而将 memo 字典作为第二个参数。

    1. # 以下两种方法都可以获得包含以3个空列表为元素的列表
    2. lists = [[]] * 3
    3. print(lists) # [[], [], []]
    4. ls = [[] for i in range(3)]
    5. print(ls) # [[], [], []]

    从图中可以看到,lists的三个元素引用自同一个对象,是重复引用。而ls中的3个元素是分3次创建的3个空列表,是3个独立的对象。两个列表虽然看似相同,但包含的对象数量是不同的。
    ls.png

    1. # 以下两种方法都可以获得包含以3个空列表为元素的列表
    2. lists = [[]] * 3
    3. lists[0].append(3)
    4. print(lists) # [[3], [3], [3]]
    5. # 上述代码中3个元素中由lists[0]重复得到,都引用自lists[0],是同一个对象。
    6. # 当这个对象的改变时,引用这个对象的所有名字的值也会发生相应的变化
    7. ls = [[] for i in range(3)]
    8. ls[0].append(3)
    9. ls[1].append(5)
    10. ls[2].append(7)
    11. print(ls) # [[3], [5], [7]]

    lists中的三个空列表是同一个对象的3次引用,所以改变其中一个值的时候,3个元素的值同时发生了变化。而ls中的三个空列表是3个独立的对象,改变列表的值的时候,相互之间无影响。

    lists.png

    1. # 以下两种方法都可以获得包含以3个空列表为元素的列表
    2. lists = [[]] * 3
    3. lists[0].append(3)
    4. lists[1].append(6)
    5. lists[2].append(9)
    6. print(lists) # [[3, 6, 9], [3, 6, 9], [3, 6, 9]]
    7. # 上述代码中3个元素中由lists[0]重复得到,都引用自lists[0],是同一个对象。
    8. # 当这个对象的改变时,引用这个对象的所有名字的值也会发生相应的变化
    9. ls = [[] for i in range(3)]
    10. ls[0].append(3)
    11. ls[1].append(5)
    12. ls[2].append(7)
    13. print(ls) # [[3], [5], [7]]

    ls2.png