Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python __具有循环引用的deepcopy_uu对象_Python_Recursion_Copy_Deep Copy - Fatal编程技术网

Python __具有循环引用的deepcopy_uu对象

Python __具有循环引用的deepcopy_uu对象,python,recursion,copy,deep-copy,Python,Recursion,Copy,Deep Copy,预期产出为: from copy import deepcopy class DoubleLinkedListNeedsDeepCopy: def __init__(self, val, tail=None): self.val = val self._next_node = None self._tail = tail or self def append(self, new_val): next_node =

预期产出为:

from copy import deepcopy

class DoubleLinkedListNeedsDeepCopy:
    def __init__(self, val, tail=None):
        self.val = val
        self._next_node = None
        self._tail = tail or self

    def append(self, new_val):
        next_node = type(self)(new_val, self._tail)
        self._next_node = next_node
        return next_node

    def __deepcopy__(self, memo):
        new_copy = type(self)(self.val)
        new_copy._next_node = deepcopy(self._next_node, memo)
        new_copy._tail = deepcopy(self._tail, memo)
        return new_copy

    @property
    def next(self):
        return self._next_node

    @property
    def tail(self):
        return self._tail

    @property
    def is_last(self):
        return self._next_node == None

linked_list = head = DoubleLinkedListNeedsDeepCopy(1)
for i in range(2, 5):
    head = head.append(i)

def print_list(linked_list):
    cur = linked_list
    for i in range(20):
        print(cur.val, end=' ')

        if cur.is_last:
            break
        else:
            cur = cur.next
    print()

import sys
sys.setrecursionlimit(10000)

print_list(linked_list)
linked_list.next.next.val = 5
print_list(linked_list)
list_copy = deepcopy(linked_list)
list_copy.next.next.val = 8
print_list(list_copy)
print_list(linked_list)
但是,在执行递归路径后,它会失败,返回递归错误:
链接列表。下一个。下一个。下一个。尾部。下一个。下一个。下一个…


(这当然是一个玩具示例,我需要在现实场景中复制一个复杂的树状结构)

事实证明,在大多数情况下(如果不需要从副本中明确排除某些字段),您只需执行
deepcopy(obj)
即使obj具有自链接或其他讨厌的属性。

虽然您决定完全避免覆盖
\uuuu deepcopy\uuu
,但实际问题仍然没有答案。我在谷歌上搜索解决方案,但没有找到任何东西,所以经过一些尝试和错误尝试后,我找到了答案,我想把它发布在这里

您编写的代码因递归错误而失败的原因是执行顺序<代码>备忘录字典仅在
\uuuu deepcopy\uuu
返回后更新。您可以在
copy.py
的源代码中检查这一点。这里是最重要的部分,没有碎片,对我们的案例来说是不必要的:

1 2 3 4 
1 2 5 4 
1 2 8 4 
1 2 5 4 
因此,我们的问题是,
备忘录
在调用另一个具有相同参数的
\uuuuu deepcopy\uuuuuu
之前不会在
\uu deepcopy\uuuu
中更新。知道了这一点,只需一行代码就可以轻松修复代码:

def deepcopy(x, memo=None, _nil=[]):
    ...

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    ...

    copier = getattr(x, "__deepcopy__", None)
    if copier:
        y = copier(memo)

    ...

    # If is its own copy, don't memoize.
    if y is not x:
        memo[d] = y
        _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y

事实证明,在这种特定情况下,use甚至根本无法覆盖
\uuuuu deepcopy\uuuu()
——而且效果很好;但是,使用递归引用对对象进行深度复制的问题仍然存在。如果覆盖
\uuuu deepcopy\uuuu
,则由您决定如何处理它。例如,您可以编写一个函数来遍历列表,跟踪它已经看到的节点,并在循环返回时停止遍历。如果你问的是如何做到这一点,那么你的问题不是关于深度复制,而是更一般地关于检测数据结构中的递归。@BrenBarn我可能遗漏了一些东西,但从这一点来看,似乎应该引用对象的一个新副本,并使用相同的
memo
dict来处理它的字段“not deepcopy”是重复的(出现在递归链接中)对象。如果这不是真的,并且必须自己遍历所有这些链接的结构和句柄,那么定义
\uu deepcopy\uu()
是否只是实现了一个复制接口,而在实际
deepcopy()的框架下没有发生任何额外的魔法
函数,然后只调用接口?
def __deepcopy__(self, memo):
    new_copy = type(self)(self.val)

    memo[id(self)] = new_copy  # THIS LINE: update memo before re-entering deep-copy machinery

    new_copy._next_node = deepcopy(self._next_node, memo)
    new_copy._tail = deepcopy(self._tail, memo)
    return new_copy