如何将一个Python对象的所有属性复制到另一个对象?

12

我有两个类,其中一个继承自另一个:

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

class DaChild(DaParent):
    additional = ''

我现在创建一个父元素并更改其属性:

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

从这一点开始,我想创建一个子元素,并从父元素复制所有属性。当然,我可以这样做:

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

问题是在开发过程中,这个类会不断增加属性,我不想一直手动复制属性到子类中。

有没有办法自动将父对象的属性继承到新的子对象中?欢迎所有提示!

[编辑] 只是为了解释我为什么要这样做。我使用 Peewee ORM 与我的数据库进行交互。现在我想对表进行修订(这意味着如果记录被更新,我希望保留所有先前版本)。我打算创建一个 Person 类和一个 PersonRevision 类,它继承自 Person 类。然后我覆盖 peewee save() 方法,不仅保存 Person 对象,而且还将所有属性复制到一个 PersonRevision 对象中并保存。由于我实际上永远不会直接与 PersonRevision 类交互,所以我不需要阴影或任何花哨的东西。我只想复制属性并调用对象的 save() 方法。


3
你是否实际想要将它们作为类属性,而不是实例属性 - jonrsharpe
据我所知,它们需要是实例属性,即对象的属性,而不是类本身的属性。 - kramer65
根据名称,您绝对不想使用继承。子类父类,但(至少一段时间内)它不是父类。 - chepner
@chepner:我认为这些名称是元语法变量 ;) - bruno desthuilliers
@kramer65:基于快速查看peewee源代码,我添加了一个“可能有效”的代码示例。同时,你应该真正了解一下“XY问题”……并不是说你的“解决方案”(最初的问题)完全错了,只是太泛泛而谈了。从一开始就陈述你的实际用例会让事情更加清晰,并避免了很多无畏的噪音。祝好运。 - bruno desthuilliers
显示剩余3条评论
3个回答

14

显而易见的解决方案是使用组合/委托而不是继承:

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

当然,这是建立在您实际上不想要“副本”,而是“引用”的前提下。如果您真的想要一份副本,也是可能的,但对于可变类型会变得棘手:

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
如果上述解决方案都不适合您的需求,请解释您的实际用例 - 您可能存在 XY 问题。
[编辑] 的确有点像 XY 问题。真正的问题是:“如何将peewee.Model的字段复制到另一个peewee.Model中。peewee使用描述符(peewee.FieldDescriptor)来控制对模型字段的访问,并将字段名称和定义存储在模型的_meta.fields字典中,因此最简单的解决方案是迭代源模型的_meta.fields键,并使用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:未经测试的代码,从未使用过 peewee,可能有更好的选择...


有没有一种好的方法可以在不同的类中重用__getattr__方法?我正在构建一些包装器,并希望在不重复代码的情况下多次使用它。 - DukeSilver
另外,值得一提的是,我修改了异常处理程序,以编程方式获取实例的类名:"{0} object has no parameter {1}".format(type(self).__name__, name)) - DukeSilver

0

我认为一个好的选择是使用父级(属性)字典(parent.__dict__)来填充子级(属性)字典(child.__dict__)。您需要一个@classmethod来从特定父级创建一个子级。下面的代码示例展示了如何在你的例子中实现:

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


class DaChild(DaParent):
    additional = ''

    @classmethod
    def from_parent(cls, parent):
        child = cls()

        for attribute_key in parent.__dict__.keys():
            child.__dict__[attribute_key] = parent.__dict__[attribute_key]

        return child


if __name__ == '__main__':
    parent = DaParent()
    parent.name = 'papa'
    parent.number = 123

    child = DaChild.from_parent(parent)

    print(child.name)
    print(child.number)

进一步解释:

@classmethod

类方法是静态方法,即:它应该从类本身(DaChild.from_parent)中调用,而不是从类的实例(child.from_parent)中调用。

它有一个变量cls作为第一个属性(就像__init__self),可以用作构造函数,如示例中的部分child = cls()

填充字典

Python对象的属性可以通过其字典访问和修改(如在部分child.__dict__[attribute_key] = parent.__dict__[attribute_key]中)。

我遍历键以获取父级的所有属性。请注意,您可以修改循环以填充所需属性,并使用if......else....进行过滤,例如过滤掉不需要的属性。


0
如果您要有一个带多个子元素的单个父元素,并且每当父元素更改时子元素应进行更新,则使父元素的属性成为类属性,子元素的“复制”将自动进行。
如果您要有多个父元素,并且至少有一些父元素将具有子元素,则有三个选项:
  • 使用委托(使子级查找其不知道的任何内容)
  • 使用复制(您现在正在执行的操作)
  • 使用元类为每个不同的父元素创建一个新的父类,然后每个父元素都将其属性作为类属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接