使用嵌套的 weakref 创建 class 实例的深层副本
Creating a deepcopy of class instance with nested weakref to it
我有两个 classes:一个父 class 和一个容器 class。父 class 实例具有匹配的容器 class 实例作为弱引用。
深度复制父实例时出现问题,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__
魔术方法可以实现,但不确定具体如何。
问题是 deepcopy
不会跟随 weakref.ref
link。它甚至不复制 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__
实例属性,有点难看。但它允许在 self
上使用正常的 deepcopy
函数,而无需进入无限递归,然后您只需要手动创建一个新的对父级的弱引用。
您甚至可以在不临时设置 __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()
我有两个 classes:一个父 class 和一个容器 class。父 class 实例具有匹配的容器 class 实例作为弱引用。
深度复制父实例时出现问题,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__
魔术方法可以实现,但不确定具体如何。
问题是 deepcopy
不会跟随 weakref.ref
link。它甚至不复制 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__
实例属性,有点难看。但它允许在 self
上使用正常的 deepcopy
函数,而无需进入无限递归,然后您只需要手动创建一个新的对父级的弱引用。
您甚至可以在不临时设置 __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()