具有循环引用的__deepcopy__对象

时间:2016-10-31 18:26:40

标签: 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 = 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)

预期输出为:

1 2 3 4 
1 2 5 4 
1 2 8 4 
1 2 5 4 

然而,在遵循递归路径后,RecursionError失败了:linked_list.next.next.next.tail.next.next.next...

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

2 个答案:

答案 0 :(得分:1)

尽管您已决定完全避免覆盖IsHitTestVisible="False",但实际问题仍未得到解答。我一直在寻找解决方案,但没有找到任何东西,因此,经过反复尝试,我找到了答案,我想在这里发布。

您编写的代码因__deepcopy__而失败的原因是执行的顺序。 RecursionError字典仅在memo返回后才更新。您可以在__deepcopy__的源代码中进行检查。这是其中最重要的部分,对于我们的情况来说是不必要的

copy.py

因此,我们的问题是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 不会在memo中进行更新,然后再使用相同的参数调用另一个__deepcopy__。知道这一点,只需一行代码即可轻松修复代码:

__deepcopy__

答案 1 :(得分:0)

事实证明,在大多数情况下(如果您不需要明确排除副本中的某些字段),即使deepcopy(obj)具有自我链接,您也可以obj其他讨厌的财产。