Python——Copy-and-Deepcopy

deepcopy 与 copy 的本质区别——是否为可变对象创建新的对象(内存空间)
以下图片截取自Python Tutorial


代码片段一

  • 可变对象 list 中包含着可变 list 和不可变对象 tuple,且 tuple 中不包含可变对象
    1
    2
    3
    4
    5
    import copy
    l1 = [3, [66, 55, 44], (7, 8, 9)]
    l2 = list(l1) # <==> l2 = copy.copy(l1)
    # <==> l2 = l1[:]
    l3 = copy.deepcopy(l1)

代码片段二

  • 可变对象 list 中包含着可变 list 和不可变对象 tuple, 且tuple中包含可变对象list
    1
    2
    3
    4
    5
    import copy
    l1 = [3, [66, 55, 44], (7, 8, [1, 2])]
    l2 = list(l1) # <==> l2 = copy.copy(l1)
    # <==> l2 = l1[:]
    l3 = copy.deepcopy(l1)

代码片段三

  • 不可变对象中包含可变对象
  • 值得强调的是,copy 复制 tuple时,不会像 list 那样直接创建新对象,无论 tuple 中是否有可变对象
  • 除非包含不可变对象,否则 deepcopy 复制 tuple 时也不会创建新对象
    1
    2
    3
    4
    5
    6
    7
    import copy
    t1 = (3, [66, 55, 44], (7, 8, [1, 2]))
    t2 = t1
    t3 = tuple(t1) # <==> t4 = t1[:]
    # t5 = copy.copy(t1)
    t6 = copy.deepcopy(t1)
    t1[1].append(100)

copy和deepcopy直接对比

  • copy.copy()是浅拷贝,copy.deepcopy()是深拷贝

浅拷贝 (copy.copy())

当你使用 copy.copy() 函数时,它会创建一个新对象,然后将原始对象中包含的所有元素引用都复制到这个新对象中。这意味着如果你修改了原对象中的可变元素(如列表、字典等),那么副本中的这些元素也会受到影响,因为它们实际上是指向同一个内存位置的引用

  • 浅拷贝示例:

    1
    2
    3
    4
    5
    6
    7
    import copy
    original_list = [[1, 2], [3, 4]]
    shallow_copy = copy.copy(original_list)
    original_list[0][0] = 'changed'

    print(original_list) # 输出: [['changed', 2], [3, 4]]
    print(shallow_copy) # 输出: [['changed', 2], [3, 4]]
    • 在这个例子中,对 original_list 的修改也影响到了 shallow_copy,因为子列表是被引用的,而不是被复制的

深拷贝 (copy.deepcopy())

  • copy.deepcopy() 函数则不仅复制了对象本身,还递归地复制了对象中包含的所有对象。因此,新的对象及其包含的所有对象都是完全独立的,对原对象或其内部对象的任何改变都不会影响到深拷贝后的对象

  • 深拷贝示例:

    1
    2
    3
    4
    5
    6
    7
    import copy
    original_list = [[1, 2], [3, 4]]
    deep_copy = copy.deepcopy(original_list)
    original_list[0][0] = 'changed'

    print(original_list) # 输出: [['changed', 2], [3, 4]]
    print(deep_copy) # 输出: [[1, 2], [3, 4]]
    • 在这个例子中,对 original_list 的修改不会影响到 deep_copy,因为所有级别的对象都被复制了

总结

  • 对于不可变对象调用 deepcopy 和 copy 函数时:
    • deepcopy 与 copy 操作一样,都不为创建新对象,而是直接引用
    • 在后面如果有修改该不可变对象的操作时再创建新对象,此时两个版本的不可变对象地址变得不同
    • 这里是 Python 的常态,比如tuple(tuple1)将返回一个 tuple1 对象的引用而不是副本,当修改 tuple1 时才会创建新对象
    • 因为无论如何,修改不可变对象的不可变部分都不会修改原始对象,所以为了节约内存,Python 解释器完全可以将创建新对象延后到修改内容时
  • 对于可变对象调用 deepcopy 和 copy 函数时:
    • 使用 copy.copy() 只复制对象的第一层,对于对象内部的嵌套对象只复制引用
    • 使用 copy.deepcopy() 会递归地复制整个对象结构,包括所有的嵌套对象,从而确保两个对象完全独立。