使用嵌套的weakref创建类实例的深层复制

时间:2017-09-13 17:13:30

标签: python python-3.5 deep-copy weak-references

我有两个类:父类和容器类。父类实例将匹配的容器类实例作为弱引用。

深度复制父实例时出现问题,weakref仍然链接到原始实例。这是一个最小的例子:

import weakref
from copy import deepcopy


class Container:    
    def __init__(self, parent):
        self.parent = weakref.ref(parent)


class Parent:
    def __init__(self):
        self.container = Container(self)


if __name__ == '__main__':
    parent1 = Parent()
    assert(parent1 is parent1.container.parent())

    parent2 = deepcopy(parent1)
    assert(parent2 is parent2.container.parent())

第二个断言失败。

我怀疑可以实现__deepcopy__魔术方法,但不确定究竟是怎么做的。

1 个答案:

答案 0 :(得分:1)

问题是deepcopy不会跟随weakref.ref链接。它甚至不复制weakref.ref

>>> from copy import deepcopy
>>> import weakref
>>>
>>> parent1 = Parent()
>>> ref1 = weakref.ref(parent1)
>>> ref2 = deepcopy(ref1)
>>> ref1 is ref2
True

明确是硬编码的in the copy module。我不知道为什么会这样,但我怀疑他们有他们的理由。

但是,您可以实施__deepcopy__方法:

import weakref
from copy import deepcopy

class Container:    
    def __init__(self, parent):
        self.parent = weakref.ref(parent)

class Parent:
    def __init__(self):
        self.container = Container(self)

    def __deepcopy__(self, memo):
        # set __deepcopy__ element to "false"-ey value so we don't go into
        # recusion.
        self.__deepcopy__ = None
        try:
            new = deepcopy(self, memo)
        finally:
            # Always delete the self.__deepcopy__ again, even if deepcopying failed
            del self.__deepcopy__
        del new.__deepcopy__  # remove the copied __deepcopy__ attribute
        new.container.parent = weakref.ref(new)
        return new

if __name__ == '__main__':
    parent1 = Parent()
    assert parent1 is parent1.container.parent()

    parent2 = deepcopy(parent1)
    assert parent2 is parent2.container.parent()

    parent3 = deepcopy(parent2)
    assert parent3 is parent3.container.parent()

由于临时__deepcopy__实例属性,它有点难看。但它允许在deepcopy上使用正常的self函数,而无需进行无限递归,然后您只需手动创建对父项的新弱引用。

您甚至可以在不临时设置__deepcopy__(它应该正常工作)的情况下执行此操作:

import weakref
from copy import deepcopy

class Container:    
    def __init__(self, parent):
        self.parent = weakref.ref(parent)

class Parent:
    def __init__(self):
        self.container = Container(self)

    def __deepcopy__(self, memo):
        # Create a new class
        new = object.__new__(type(self))
        memo[id(self)] = new   # add the new class to the memo
        # Insert a deepcopy of all instance attributes
        new.__dict__.update(deepcopy(self.__dict__, memo))
        # Manually update the weakref to be correct
        new.container.parent = weakref.ref(new)
        return new

if __name__ == '__main__':
    parent1 = Parent()
    assert parent1 is parent1.container.parent()

    parent2 = deepcopy(parent1)
    assert parent2 is parent2.container.parent()

    parent3 = deepcopy(parent2)
    assert parent3 is parent3.container.parent()