Frank-Rene Schäfer
Frank-Rene Schäfer

Reputation: 3352

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.

Upvotes: 1

Views: 2618

Answers (1)

finefoot
finefoot

Reputation: 11232

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.

Upvotes: 1

Related Questions