Python 深拷贝与浅拷贝

在 Python 中,当我们需要复制对象时,有两种方式:深拷贝和浅拷贝。深拷贝会创建一个新的对象,同时复制对象中的所有子对象,而浅拷贝仅复制对象的引用。在本文中,我们将详细讨论这两种拷贝方式,并提供一些代码示例。

浅拷贝

浅拷贝是指在内存中创建一个新的对象,该对象与原始对象具有相同的值。然而,原始对象和新对象共享相同的子对象。这意味着,如果我们改变原始对象中的子对象,新对象也会受到影响。浅拷贝可以通过使用切片操作符([:])或 copy() 方法来实现。

下面是使用切片操作符进行浅拷贝的示例代码:

original_list = [1, 2, [3, 4]]
shallow_copy = original_list[:]

我们可以通过修改原始列表中的子列表来验证浅拷贝的行为:

original_list[2].append(5)
print(original_list)  # 输出 [1, 2, [3, 4, 5]]
print(shallow_copy)  # 输出 [1, 2, [3, 4, 5]]

可以看到,当我们修改原始列表中的子列表时,浅拷贝副本也会受到影响。

深拷贝

与浅拷贝不同,深拷贝会创建一个全新的对象,并复制对象中的所有子对象。这意味着,原始对象和新对象是完全独立的,互不影响。在 Python 中,我们可以使用 copy.deepcopy() 方法来执行深拷贝操作。

下面是使用 copy.deepcopy() 方法进行深拷贝的示例代码:

import copy

original_list = [1, 2, [3, 4]]
deep_copy = copy.deepcopy(original_list)

我们可以通过修改原始列表中的子列表来验证深拷贝的行为:

original_list[2].append(5)
print(original_list)  # 输出 [1, 2, [3, 4, 5]]
print(deep_copy)  # 输出 [1, 2, [3, 4]]

可以看到,当我们修改原始列表中的子列表时,深拷贝副本不受影响。

类图

下面是深拷贝和浅拷贝的类图示例:

classDiagram
    class Object
    class ShallowCopy
    class DeepCopy

    Object <|-- ShallowCopy
    Object <|-- DeepCopy

在上面的类图中,Object 是所有对象的基类。ShallowCopyDeepCopy 是两个派生类,用于表示浅拷贝和深拷贝。

走进源码

在 Python 中,浅拷贝和深拷贝的实现主要依赖于 copy 模块。下面是 copy 模块的部分源码:

def copy(x):
    cls = type(x)

    # 通过 __copy__ 魔法方法进行拷贝
    copier = getattr(x, "__copy__", None)
    if copier:
        return copier()

    # 创建一个新对象
    if isinstance(x, _Array):
        return x.copy()
    copier = _copy_dispatch.get(cls)
    if copier:
        return copier(x)

    # 如果对象是可迭代的,则创建一个新的迭代器对象
    if issubclass(cls, type({}.keys())):
        return cls(x)

    # 创建一个空对象,并将原始对象的属性复制到新对象中
    reductor = dispatch_table.get(cls)
    if reductor:
        rv = reductor(x)
    else:
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor:
            rv = reductor(4)
        else:
            reductor = getattr(x, "__reduce__", None)
            if reductor:
                rv = reductor()
            else:
                raise Error("un(shallow)copyable object of type %s" % cls)

    return _reconstruct(x, rv, 0)

可以看到,拷