How to copy a python class?

Python

Python Problem Overview


deepcopy from copy does not copy a class:

>>> class A(object):
>>>     ARG = 1
   
>>> B = deepcopy(A)

>>> A().ARG
>>> 1

>>> B().ARG
>>> 1

>>> A.ARG = 2

>>> B().ARG
>>> 2

Is it only way?

B(A):
    pass

Python Solutions


Solution 1 - Python

In general, inheritance is the right way to go, as the other posters have already pointed out.

However, if you really want to recreate the same type with a different name and without inheritance then you can do it like this:

class B(object):
    x = 3

CopyOfB = type('CopyOfB', B.__bases__, dict(B.__dict__))

b = B()
cob = CopyOfB()

print b.x   # Prints '3'
print cob.x # Prints '3'

b.x = 2
cob.x = 4

print b.x   # Prints '2'
print cob.x # Prints '4'

You have to be careful with mutable attribute values:

class C(object):
    x = []

CopyOfC = type('CopyOfC', C.__bases__, dict(C.__dict__))

c = C()
coc = CopyOfC()

c.x.append(1)
coc.x.append(2)

print c.x   # Prints '[1, 2]' (!)
print coc.x # Prints '[1, 2]' (!)

Solution 2 - Python

The right way to "copy" a class, is, as you surmise, inheritance:

class B(A):
    pass

Solution 3 - Python

You could use a factory function:

def get_A():
    class A(object):
        ARG = 1
    return A

A = get_A()
B = get_A()

Solution 4 - Python

As Florian Brucker pointed out, there is a problem with mutable class attributes. You also can't just deepcopy(cls.__dict__) on new style objects. I've done the following to solve this problem for what I'm doing. I'm certain someone determined enough could break this. But, it will work in more cases.

from copy import deepcopy
from typing import TypeVar

Cls = TypeVar('Cls')


# This type hint is a dirty lie to make autocomplete and static
# analyzers give more useful results. Crazy the stuff you can do
# with python...
def copy_class(cls: Cls) -> Cls:
    copy_cls = type(f'{cls.__name__}Copy', cls.__bases__, dict(cls.__dict__))
    for name, attr in cls.__dict__.items():
        try:
            hash(attr)
        except TypeError:
            # Assume lack of __hash__ implies mutability. This is NOT
            # a bullet proof assumption but good in many cases.
            setattr(copy_cls, name, deepcopy(attr))
    return copy_cls


def test_copy_class():
    class A(object):
        mutable_class_var = []

    ACopy = copy_class(A)

    a = A()
    acopy = ACopy()

    acopy.mutable_class_var.append(1)
    assert a.mutable_class_var == []
    assert A.mutable_class_var == []
    assert ACopy.mutable_class_var == [1]
    assert acopy.mutable_class_var == [1]

Solution 5 - Python

I think you misunderstand the meaning of static variable here. Every where you declare a variable outside a method and not in the shape of self.some_thing, the variable will be considered as class's static variable ( like your ARG variable here). Thus, every object ( instance ) of the Class that changes a static variable will cause change of all other objects in the same Class. The deepcopy really does the job here.

Solution 6 - Python

To copy a class with __slots__ attribute, this function will help :-)

def copy_class(c,name=None):
    if not name: name = 'CopyOf'+c.__name__
    if hasattr(c,'__slots__'):
        slots = c.__slots__ if type(c.__slots__) != str else (c.__slots__,)
        dict_ = dict()
        sloted_members = dict()
        for k,v in c.__dict__.items():
            if k not in slots:
                dict_[k] = v
            elif type(v) != types.MemberDescriptorType:
                sloted_members[k] = v
        CopyOfc = type(name, c.__bases__, dict_)
        for k,v in sloted_members.items():
            setattr(CopyOfc,k,v)
        return CopyOfc
    else:
        dict_ = dict(c.__dict__)
        return type(name, c.__bases__, dict_)

Solution 7 - Python

A simple approach is to put the class in a module and reload it every time you want a new copy. I think this deals with the mutables, because the reload recreates everything.

Solution 8 - Python

This is a solution for copying at all levels:

#for windows use dill teh same way

import pickle
copy = lambda obj: pickle.loads(pickle.dumps(obj))

Problem was:

class A: a = 1
x = A()
y = x
x.a = 5
print(y.a) #return's 5

With copy:

class A:a = 1
x = A()
y = copy(x)
x.a = 5
print(y.a) #return 1

You wan copy anything you want, not only class instances or Classes

Solution 9 - Python

If you want to create just another instance of class then just make it:

 >>> class A(object):
...    ARG=1
... 
 >>> a = A()
 >>> A().ARG
 1
 >>> b = A()
 >>> b.ARG
 1
 >>> a.ARG=2
 >>> b.ARG
 1

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionI159View Question on Stackoverflow
Solution 1 - PythonFlorian BruckerView Answer on Stackoverflow
Solution 2 - PythonDavid RobinsonView Answer on Stackoverflow
Solution 3 - PythonJamesView Answer on Stackoverflow
Solution 4 - PythonwilltalmadgeView Answer on Stackoverflow
Solution 5 - PythonvutranView Answer on Stackoverflow
Solution 6 - PythonnkproView Answer on Stackoverflow
Solution 7 - PythonM JuckesView Answer on Stackoverflow
Solution 8 - PythonHenryView Answer on Stackoverflow
Solution 9 - PythonPavel ShvedovView Answer on Stackoverflow