ZetCode

Python __deepcopy__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨了 Python 的 __deepcopy__ 方法,这是一种特殊方法,可以为对象启用自定义的深拷贝行为。我们将介绍基本用法、实现模式和实际示例。

基本定义

__deepcopy__ 方法由 copy.deepcopy() 调用,用于实现深拷贝操作。它必须定义如何创建对象的深拷贝,包括所有嵌套对象。

主要特征:它接受一个备忘录字典来处理循环引用,返回对象的一个新的独立副本,并且应该递归地深拷贝所有可变属性。它提供了对深拷贝过程的完全控制。

基本 __deepcopy__ 实现

这是一个简单的实现,展示了 __deepcopy__ 如何与 copy 模块一起工作。它演示了基本结构和备忘录用法。

basic_deepcopy.py
import copy

class Box:
    def __init__(self, items):
        self.items = items
    
    def __deepcopy__(self, memo):
        print("Performing deep copy of Box")
        new_items = copy.deepcopy(self.items, memo)
        new_box = Box(new_items)
        memo[id(self)] = new_box
        return new_box

original = Box([[1, 2], [3, 4]])
copied = copy.deepcopy(original)
print(copied.items)

这个例子展示了一个带有嵌套列表的 Box 类。__deepcopy__ 方法创建了一个新的 Box,其中包含其项目的深拷贝。备忘录字典有助于防止循环引用的无限递归。

该方法首先创建项目的深拷贝,然后使用这些拷贝的项目创建一个新的 Box 实例。原始对象和拷贝对象是完全独立的。

处理循环引用

__deepcopy__ 中的备忘录字典通过跟踪已经拷贝的对象来帮助处理循环引用。 这可以防止无限递归。

circular_reference.py
import copy

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None
    
    def __deepcopy__(self, memo):
        if id(self) in memo:
            return memo[id(self)]
            
        print(f"Copying node {self.value}")
        new_node = Node(copy.deepcopy(self.value, memo))
        memo[id(self)] = new_node
        new_node.next = copy.deepcopy(self.next, memo)
        return new_node

# Create circular reference
node1 = Node(1)
node2 = Node(2)
node1.next = node2
node2.next = node1

copied = copy.deepcopy(node1)
print(copied.next.next.value)  # 1

这个 Node 类形成了一个循环引用。 __deepcopy__ 方法在拷贝之前检查备忘录字典,以正确处理循环引用。 如果没有此检查,它将无限递归。

备忘录通过对象的 id 存储已经拷贝的对象。 当遇到已经拷贝的节点时,它返回存储的副本而不是递归。

自定义深拷贝行为

__deepcopy__ 允许自定义如何拷贝特定属性,这在某些属性不应被深拷贝时非常有用。

custom_behavior.py
import copy

class Config:
    def __init__(self, params, logger):
        self.params = params
        self.logger = logger  # Should not be deep copied
    
    def __deepcopy__(self, memo):
        print("Creating custom deep copy of Config")
        new_params = copy.deepcopy(self.params, memo)
        new_config = Config(new_params, self.logger)
        memo[id(self)] = new_config
        return new_config

logger = object()
original = Config({"timeout": 30, "retries": 3}, logger)
copied = copy.deepcopy(original)
print(copied.params)
print(copied.logger is original.logger)  # True

这个 Config 类演示了选择性的深拷贝。 params 字典被深拷贝,但 logger 引用在原始对象和拷贝对象之间共享。

当某些属性表示不应在拷贝操作期间复制的共享资源或单例时,此模式非常有用。

带有继承的深拷贝

在继承层次结构中实现 __deepcopy__ 时,您需要正确处理父类属性和备忘录字典。

inheritance.py
import copy

class Base:
    def __init__(self, base_value):
        self.base_value = base_value
    
    def __deepcopy__(self, memo):
        print("Deep copying Base")
        new_base = self.__class__(copy.deepcopy(self.base_value, memo))
        memo[id(self)] = new_base
        return new_base

class Derived(Base):
    def __init__(self, base_value, derived_value):
        super().__init__(base_value)
        self.derived_value = derived_value
    
    def __deepcopy__(self, memo):
        print("Deep copying Derived")
        new_obj = super().__deepcopy__(memo)
        new_obj.derived_value = copy.deepcopy(self.derived_value, memo)
        return new_obj

original = Derived([1, 2], {"a": 1})
copied = copy.deepcopy(original)
print(copied.base_value, copied.derived_value)

这个例子展示了带有继承的正确的 __deepcopy__ 实现。 Derived 类首先委托给父类的 __deepcopy__,然后处理自己的属性。

备忘录字典通过整个拷贝过程传递,以保持继承层次结构中的一致性并处理潜在的循环引用。

性能优化

可以通过避免不必要的不可变对象拷贝或为复杂结构实现自定义拷贝逻辑来优化 __deepcopy__

optimization.py
import copy

class LargeData:
    def __init__(self, data, metadata):
        self.data = data  # Large, complex structure
        self.metadata = metadata  # Small, simple structure
    
    def __deepcopy__(self, memo):
        print("Optimized deep copy of LargeData")
        # Skip deep copying metadata as it's small and contains only immutables
        new_metadata = self.metadata
        # Custom optimized copy for large data
        new_data = [copy.deepcopy(item, memo) for item in self.data]
        new_obj = self.__class__(new_data, new_metadata)
        memo[id(self)] = new_obj
        return new_obj

original = LargeData([[1, 2, 3]] * 1000, {"created": "2025-01-01"})
copied = copy.deepcopy(original)
print(len(copied.data), copied.metadata)

这种优化后的实现避免了对元数据字典的深拷贝,因为它只包含不可变值。 它还为大型数据结构使用列表推导式,以使拷贝过程更加高效。

在处理标准深拷贝过于缓慢或内存密集的大型对象时,这种优化非常有价值。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程