Deepcopy on two classes containing eachother gives recursion error. Python - python

When I have a object-structure like this:
from copy import deepcopy
class A:
def __init__(self, b):
self.b = b
def __deepcopy__(self, memodict):
return A(deepcopy(self.b, memodict))
class B:
def __init__(self, a):
self.a = a
def __deepcopy__(self, memodict):
return B(deepcopy(self.a, memodict))
test_a = A(None)
test_b = B(None)
test_a.b = test_b
test_b.a = test_a
copy_a = deepcopy(test_a)
And I try to make a deepcopy of a object I get a "maximum recursion depth exceeded" error.
Which I understand why this happens but I don't know what the best approach would be to solve this?
Help much appreciated

You should not override __deepcopy__, just let deepcopy function do its work.
By the way I had to remove annotation :B because its a foreward reference and gives name error.
from copy import deepcopy
class A:
def __init__(self, b):
self.b = b
class B:
def __init__(self, a):
self.a = a
a = A(None)
b = B(None)
a.b = b
b.a = a
aa = deepcopy(a)
print (aa is a) # -> False
print(aa.b is b) # -> False
print(aa.b.a is aa) # -> True
But if you for any reason want to override the __deepcopy__ you should do it like this:
from copy import deepcopy
class A:
def __init__(self, b):
self.b = b
def __deepcopy__(self, memodict):
a = A(None)
memodict[id(self)] = a
a.b = deepcopy(self.b, memodict)
return a
class B:
def __init__(self, a: A):
self.a = a
def __deepcopy__(self, memodict):
b = B(None)
memodict[id(self)] = b
b.a = deepcopy(self.a, memodict)
return b
a = A(None)
b = B(None)
a.b = b
b.a = a
aa = deepcopy(a)
print(aa is a) # -> False
print(aa.b is b) # -> False
print(aa.b.a is aa) # -> True
ref: https://stackoverflow.com/a/15774013/1951448

Related

using __new__ to return object from different classes depending on input

I wanted to override __new__ function, for a specific class, in such a way that depending on the input passed, it returns an object from a class or from another.
I wrote the following code - which seems to work - but it feels like cheating and I wanted to know if this is the "best" or the "most python-like" way to do:
class A:
def __new__(cls, x):
if x == 1:
a = 0
self = B.__new__(A, a)
self.cls = B
else:
a = 0
b = 0
self = C.__new__(A, a, b)
self.cls = C
return self
def __init__(self, x):
self.__class__ = self.cls
class B(A):
def __new__(cls, a):
self = object.__new__(cls)
self.a = a
return self
def print_this(self):
print("self.a is: ", self.a)
print("class is B")
class C(A):
def __new__(cls, a, b):
self = object.__new__(cls)
self.a = a
self.b = b
return self
def print_this(self):
print("self.a is: ", self.a)
print("self.b is: ", self.b)
print("class is C")
xxx = A(1)
print("xxx.__class__: ", xxx.__class__)
xxx.print_this()
yyy = A(2)
print("yyy.__class__: ", yyy.__class__)
yyy.print_this()
it returns the following, which is what I wanted
xxx.__class__: <class '__main__.B'>
self.a is: 0
class is B
yyy.__class__: <class '__main__.C'>
self.a is: 0
self.b is: 0
class is C
Yes, it's dirty - it's not obvious for a reader that A() actually creates instance of another class, if someone change something in B.__new__ he can break parts he would never expects and could be hard to understand for non-experienced python programmers what is going on here.
What you search for is factory design pattern.
from enum import Enum
class ClsEnum(Enum):
B = "B"
C = "C"
class A:
pass
def cls_factory(e: ClsEnum) -> A:
if e == ClsEnum.B:
return B(0)
elif e == ClsEnum.C:
return C(0, 0)
raise TypeError
class B(A):
def __init__(self, a):
self.a = a
def print_this(self):
print("self.a is: ", self.a)
print("class is B")
class C(A):
def __init__(self, a, b):
self.a = a
self.b = b
def print_this(self):
print("self.a is: ", self.a)
print("self.b is: ", self.b)
print("class is C")
xxx = cls_factory(ClsEnum.B)
print("xxx.__class__: ", xxx.__class__)
xxx.print_this()
yyy = cls_factory(ClsEnum.C)
print("yyy.__class__: ", yyy.__class__)
yyy.print_this()
This code gives the same output as your version.

Pass an object as a list in subclass' constructor in python

How do I implement the Subclass B for the function getdata() to return the details in the constructor? I could achieve that if the constructor in B takes a single object, but having difficulty if the objects were two or more and passed as a list.
This example shows what I intended. Code lacks the full implementation.
class A(object):
def __init__(self, a):
self.a = a
def geta(self):
return str(self.a)
class B(A):
def __init__(self, b,c, [objA1, objA2]):
self.b = b
self.c = c
super().__init__()
# ...
def geta(self):
return str(self.a)
def getb(self):
return str(self.b)
def getdata(self):
return str(self.geta()) + str(self.getb()) + ...
obja1 = A('John')
obja2 = A('Doe')
obj = B('123', '456', [obja1, obja2])
# Test1 obj.getdata() == "John Doe 123 456"
You could do like this and pass as normal arguments and then convert into list
class B(A):
def __init__(self,b,objA1,objA2):
self.b = b
self.list = [objA1, objA2]
super().__init__()
obj = B("hello", "JOHN","DOE")
like this works aswell, basicaly u say that objs will be a list
class B(A):
def __init__(self,b, objs: list):
self.b = b
self.list = objs
super().__init__()
obj = B("hello", ["JOHN","DOE"])
So you don't need inheritance here. Composition will be enough.
This code produces output according to your requirements:
class A:
def __init__(self, a):
self.a = a
def __repr__(self):
return str(self.a)
class B:
def __init__(self, b, c, others):
self.b = b
self.c = c
self.others = others
def getdata(self):
items = self.others + [self.b, self.c]
return ' '.join([str(item) for item in items])
obja1 = A('John')
obja2 = A('Doe')
obj = B('123', '456', [obja1, obja2])
print(obj.getdata())
Output:
John Doe 123 456

How to deep-copy a class object in Python3

GIVEN:
class A:
x = 4711
B = COPY(A)
setattr(B, "x", "0815")
print("A: %s; B: %s;" % (A.x, B.x))
GOAL:
An operation COPY such that the code fragment above results in
A: 4711; B: 0815;
IN PLAIN WORDS:
By what means can a class object be deep-copied, so that it is totally isolated from its original. Using copy.deepcopy() delivers
A: 0185; B: 0185;
so that is not the solution.
from copy import deepcopy
class A:
x = 123
def __init__(self):
self.f()
def f(self):
print("original function", self.x)
def g(self):
print("replacement function", self.x)
B = deepcopy(A)
B.x = 456
B.f = g
a = A()
b = B()
This example prints:
replacement function 456
replacement function 456
Apparently, both A as well as B share the same values for their attributes x and f. Therefore, as you have already noted, copy.deepcopy doesn't work to "copy" a class object. It seems that Python class objects are singletons, because A is deepcopy(A) is True.
So, alternatively, you could just use inheritance instead of copying:
class A:
x = 123
def __init__(self):
self.f()
def f(self):
print("original function", self.x)
def g(self):
print("replacement function", self.x)
class B(A):
pass
B.x = 456
B.f = g
a = A()
b = B()
Which prints:
original function 123
replacement function 456
Like this, we are able to change B.x and B.f without affecting A.x and A.f.
However, isinstance(b, A) will be True, which might be undesired. Also, changes to class attributes of A will propagate to its child B. Therefore, you just change your original A into a dummy A_, first, and then derive both A and B from that:
class A:
x = 123
def __init__(self):
self.f()
def f(self):
print("original function", self.x)
def g(self):
print("replacement function", self.x)
A_ = A
class A(A_):
pass
class B(A_):
pass
B.x = 456
B.f = g
a = A()
b = B()
Now, isinstance(b, A) will be False and changes to class attributes of A will not propagate to B.

Pass String Parameter into Class/Function (Python)

If I have a class as such:
class Sample:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
I can create an object by:
temp = Sample(a=100,b=100,c=100)
But what if I have:
my_str = "a=100,b=100,c=100"
How can I temp = Sample(my_str) properly?
You can parse and eval the string like:
Code:
#classmethod
def from_str(cls, a_str):
return cls(**eval("dict({})".format(a_str)))
Test Code:
class Sample:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
#classmethod
def from_str(cls, a_str):
return cls(**eval("dict({})".format(a_str)))
x = Sample.from_str("a=100,b=100,c=100")
print(x.a)
Results:
100
use eval
temp = eval("Sample("+my_str+")")
Although it is definitely an option, using eval can be dangerous. Here is an option which is #StephenRauch's code just without using eval.
>>> class Sample:
... def __init__(self, a, b, c):
... self.a = a
... self.b = b
... self.c = c
...
... #classmethod
... def from_str(cls, a_str):
... result = {}
... for kv in a_str.split(','):
... k, v = kv.split('=')
... result[k] = int(v)
... return cls(**result)
...
>>> x = Sample.from_str('a=100,b=100,c=100')
>>> x.a
100
>>> type(x.a)
<class 'int'>
You can use the below code.
class Sample:
def __init__(self, a, b, c):
self.a = int(a)
self.b = int(b)
self.c = int(c)
mystr = "a=100,b=100,c=100"
temp = Sample(mystr.split(",")[0].split("=")[1],mystr.split(",")[1].split("=")[1],mystr.split(",")[2].split("=")[1])
print(temp.a)
print(temp.b)
print(temp.c)
See it in action here
This works for me:
my_str = "a=100,b=100,c=100"
temp = Sample(int(my_str.split(',')[0].split('=')[1]),
int(my_str.split(',')[1].split('=')[1]),
int(my_str.split(',')[2].split('=')[1]))
print(temp.a)
# prints 100
print(temp.b)
# prints 100
print(temp.c)
# prints 100

python classes dependant on eachother, how to init?

I have two classes:
class A(object):
def __init__(self, b):
self b = b
class B(object):
def __init__(self, a):
self a = a
I'd like to init them like this:
a = A(b)
b = B(a)
But I can't since 'b' doesn't exist when doing a = A(b). I have to do:
a = A()
b = B(a)
b.a = a
But that seems unclean. Is this solvable?
You could either make one class instantiate the other:
class A(object):
def __init__(self):
self.b = B(self)
class B(object):
def __init__(self, a):
self.a = a
a = A()
b = a.b
Or make one class tell the other about itself, like this:
class A(object):
def __init__(self, b):
self.b = b
b.a = self
class B(object):
def __init__(self):
#Will be set by A later
self.a = None
b = B()
a = A(b)

Categories