【问题标题】:How to copy all attributes of one Python object to another?如何将一个 Python 对象的所有属性复制到另一个对象?
【发布时间】:2014-12-15 13:09:44
【问题描述】:

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

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

class DaChild(DaParent):
    additional = ''

我现在创建一个 Parent 并更改属性:

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

从这一点开始,我想创建一个 Child,我想在其中复制父级的所有属性。我当然可以这样做:

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

问题是,在开发过程中,这个类会增长到拥有相当多的属性,我不想经常改变手动将属性复制到孩子中。

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

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

【问题讨论】:

  • 你真的希望那些是 class,而不是 instance 属性吗?
  • @jonrsharpe - 据我了解,它们需要是实例属性,因此是对象的属性,而不是类本身。
  • 不是你现在拥有的——在class内部但在实例方法def外部定义的属性是类属性。跨度>
  • 试图确定我理解:孩子应该跟随父母吗? IE。如果 parent.name 改变,child.name 也应该改变吗?
  • 根据名称,您肯定不想使用继承。孩子父母,但它不是(至少在一段时间内)不是父母。

标签: python class oop object inheritance


【解决方案1】:

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

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 问题。

[edit] 确实与 XY 问题接壤。真正的问题是:“我如何将peewee.Model 的字段复制到另一个peewee.Modelpeewee 使用描述符 (peewee.FieldDescriptor) 来控制对模型字段的访问,并将字段名称和定义存储在模型的_meta.fields dict,所以最简单的解决方案是迭代源模型的 _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")

注意:未经测试的代码,从未使用过peewee,可能还有更好的事情要做...

【讨论】:

  • 有没有在不同类中重用__getattr__方法的好方法?我正在构建一些包装器,并希望在不重复代码的情况下多次使用。
  • 另外,为了它的价值,我修改了异常以编程方式获取实例的类名:"{0} object has no parameter {1}".format(type(self).__name__, name))
【解决方案2】:

如果你打算有一个有多个孩子的单亲,并且无论何时父母发生变化,都应该更新孩子以匹配父母,那么让父母的属性改为类属性,并且“复制”到孩子将是自动。

如果您打算有多个父母,并且至少有一些父母会有孩子,那么有三种选择:

  • 使用委托(让孩子在父母身上寻找任何它不知道的东西
  • 使用复制(你现在正在做什么)
  • 使用元类为每个不同的父类创建一个新的父类,然后每个父类都有自己的属性作为类属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    相关资源
    最近更新 更多