深入探究:如何解决深拷贝中的循环引用问题?

9 min read

深拷贝中存在循环引用是一个很常见的问题,因为在拷贝的过程中会涉及到引用的复制,如果不处理好循环引用,会导致拷贝过程进入死循环。

解决循环引用的关键在于记录已经拷贝过的对象,以及在第二次拷贝到该对象时直接返回已拷贝的对象的引用。

下面是一个示例代码,展示如何通过递归和字典记录已拷贝的对象来处理循环引用问题:

import copy

def deep_copy(obj, memo=None):
    if memo is None:
        memo = {}
        
    # 检查该对象是否已经被拷贝过
    if id(obj) in memo:
        return memo[id(obj)]
    
    # 如果是原始数据类型,则直接返回
    if isinstance(obj, (int, float, str, bool, type(None))):
        return obj
    
    # 如果是列表或元组,则递归拷贝其中的每个元素
    if isinstance(obj, (list, tuple)):
        copy_obj = []
        memo[id(obj)] = copy_obj
        for item in obj:
            copy_obj.append(deep_copy(item, memo))
            
        if isinstance(obj, tuple):
            copy_obj = tuple(copy_obj)
            
        return copy_obj
    
    # 如果是字典,则递归拷贝每个键值对
    if isinstance(obj, dict):
        copy_obj = {}
        memo[id(obj)] = copy_obj
        for key, value in obj.items():
            copy_obj[deep_copy(key, memo)] = deep_copy(value, memo)
            
        return copy_obj
    
    # 如果是其它对象(如自定义类),则调用该对象的 __dict__ 属性来拷贝
    if hasattr(obj, "__dict__"):
        copy_obj = type(obj)()
        memo[id(obj)] = copy_obj
        for key, value in obj.__dict__.items():
            setattr(copy_obj, key, deep_copy(value, memo))
            
        return copy_obj
    
    # 如果仍然遇到未知类型,则抛出异常
    raise ValueError("unsupported type: %r" % obj)

其中 memo 参数为字典类型,用于记录已经拷贝过的对象和其对应的拷贝对象。

在拷贝对象时,先判断该对象是否已经被记录在 memo 中。如果已经存在,则直接返回已拷贝的对象的引用;否则,先将该对象在 memo 中记录下来,并递归拷贝该对象的每个元素,然后将拷贝得到的新元素存到 memo 中,并返回拷贝后的对象。