I have two classes: a parent class and a container class. The parent class instance has matching container class instance as a weak reference.
There is a problem while deep copying the parent instance, the weakref is still linking to the original instance. Here is a minimal example:
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())
The second assertion fails.
I suspect that the __deepcopy__
magic method can be implemented, but not sure how exactly.
The problem is that deepcopy
won't follow the weakref.ref
link. It doesn't even copy the weakref.ref
:
>>> from copy import deepcopy
>>> import weakref
>>>
>>> parent1 = Parent()
>>> ref1 = weakref.ref(parent1)
>>> ref2 = deepcopy(ref1)
>>> ref1 is ref2
True
That is explicitly hardcoded in the copy
module. I don't know why this is but I suspect they had their reasons.
However you can implement a __deepcopy__
method:
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()
It's a bit ugly because of the temporary __deepcopy__
instance attribute. But it allows to use the normal deepcopy
function on self
without going into infinite recursions and then you only have to manually create a new weak reference to your parent.
You could even do it without temporarily setting __deepcopy__
(it should work correctly):
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With