Skip to content
Advertisement

How to copy all attributes of one Python object to another?

I’ve got two classes, of which one inherits from the other:

class DaParent(object):
    name = ''
    number = 0

class DaChild(DaParent):
    additional = ''

I now create a Parent and change the attributes:

parent = DaParent()
parent.name = 'papa'
parent.number = 123

And from this point, I want to create a Child in which I want to copy all the attributes from the parent. I can of course do this like so:

child = DaChild()
child.name = parent.name
child.number = parent.number

The thing is that while developing, this class will grow to have a fairly large number of attributes, and I don’t constantly want to change the manual copying of the attributes into the child.

Is there a way to automatically take over the attributes of the parent object into a new child object? All tips are welcome!

[EDIT] Just to explain the WHY I want to do this. I use the Peewee ORM to interact with my DB. I now want to revision a table (meaning if a record gets updated, I want keep all previous versions). The way I intent to do that is by for example creating a Person class and a PersonRevision class which inherits from the Person class. I then override the peewee save() method to not only save the Person object, but also copy all attributes into a PersonRevision object and save that as well. Since I will never actually directly interact with the PersonRevision class I don’t need shadowing or any fancy stuff. I just want to copy the attributes and call the object its save() method.

Advertisement

Answer

The obvious solution is to use composition/delegation instead of inheritence:

class Parent(object):
   def __init__(self, name, number):
       self.name = name
       self.number = number


class Child(object):
    def __init__(self, parent, other):
        self.parent = parent
        self.other = other

    def __getattr__(self, name):
        try:
            return getattr(self.parent, name)
        except AttributeError, e:
            raise AttributeError("Child' object has no attribute '%s'" % name)

p = Parent("Foo", 42)
c = Child(p, "parrot")
print c.name, c.number, c.other
p.name = "Bar"
print c.name, c.number, c.other

This is of course assuming that you dont really want “copies” but “references to”. If you really want a copy it’s also possible but it can get tricky with mutable types:

import copy

class Parent(object):
   def __init__(self, name, number):
       self.name = name
       self.number = number


class Child(object):
    def __init__(self, parent, other):
        # only copy instance attributes from parents
        # and make a deepcopy to avoid unwanted side-effects
        for k, v in parent.__dict__.items():
            self.__dict__[k] = copy.deepcopy(v)
        self.other = other

If none of these solutions fit your needs, please explain your real use case – you may have an XY problem.

[edit] Bordering on a XY problem, indeed. The real question is: “How do I copy a peewee.Model‘s fields into another peewee.Model. peewee uses descriptors (peewee.FieldDescriptor) to control access to model’s fields, and store the fields names and definitions in the model’s _meta.fields dict, so the simplest solution is to iterate on the source model’s _meta.fields keys and use getattr / setattr:

class RevisionMixin(object):
    @classmethod
    def copy(cls, source, **kw):
        instance = cls(**kw)
        for name in source._meta.fields:
            value = getattr(source, name)
            setattr(instance, name, value)
        return instance

class Person(peewee.Model):
     # fields defintions here


class PersonRevision(Person, RevisionMixin):
    # additional fields definitions here


p = Person(name="foo", number=42)
r = PersonRevision.copy(p, whatelse="parrot")

NB : untested code, never used peewee, there’s possibly something better to do…

Advertisement