How to copy a python class?

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

Answers


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]' (!)

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

class B(A):
    pass

You could use a factory function:

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

A = get_A()
B = get_A()

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.


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]

Create your own constructor, and just duplicate how you made your original class:

class MyClass(object):
    def __init__(self, **kw):
        self.kw = kw
        self.__dict__.update(kw)

    def copy(self):
        return MyClass(**self.kw)

I would try to avoid the need to copy objects in the first place though.


sidenote: You might also be able to get away with doing:

B = deepcopy(A)
B.__dict__ = deepcopy(A.__dict__)

But this is probably very very wrong and you should not do this. edit: actually AttributeError: 'dictproxy' object has no attribute 'update' according to the OP


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

Need Your Help

HTML into PHP Variable (HTML outside PHP code)

php output-buffering

I am new to php and wondering if I can have something like this:

Getting PHPUnit Working - Include Path not set correctly?

php unit-testing phpunit pear

I'm trying to get PHPUnit working on my development environment but I've hit a bit of a roadblock when it comes to including PHPUnit in my scripts. I know that I need to set the include path on PHP...