【问题标题】:Python : How to "merge" two classPython:如何“合并”两个类
【发布时间】:2012-03-12 13:21:38
【问题描述】:

我想在各种类中添加一些属性和方法。我必须添加的方法和属性是相同的,但不是分配它们的类,所以我想构造一个类,为参数中给定的类分配新的方法和属性。 我试试这个,但它不工作: (我知道尝试为自己分配一些东西是一种非常错误的方式,它只是为了显示我想要做什么)

class A:
    def __init__(self):
        self.a = 'a'

    def getattA(self):
        return self.a

class B:
    def __init__(self, parent) :
        self = parent

        # This is working :
        print self.getattA()

    def getattB(self):
        return self.getattA()

insta = A()
instb = B(insta)

# This is not working :
print instb.getattB()

结果是:

a
Traceback (most recent call last):
  File "D:\Documents and settings\Bureau\merge.py", line 22, in <module>
    print instb.getattB()
  File "D:\Documents and settings\Bureau\merge.py", line 16, in getattB
    return self.getattA()
AttributeError: B instance has no attribute 'getattA'

我希望在调用 instb.gettattB() 时得到“a”

要恢复,我想从 A 类继承 B 类,在 B 类的参数中给出 A 类,因为我的 B 类将是各种类的子类,而不总是 A。

【问题讨论】:

  • 我不明白你在做什么,你能提供一个完整的例子和预期的结果吗?不过,至少如果您计划支持 Python 2.X,请使用 class A(object)class B(object)
  • #这不起作用 - 它怎么不起作用?
  • 请贴出运行代码时得到的输出。
  • In [5]: dir(instb) Out[5]: [__doc__, __init__, __module__, 'getattB'] 如果您使用 instb 对象,则没有 getatt 方法想要它,你应该从 A 类继承 B 类
  • self 只能在 __init__ 方法中访问。您将不得不使用self.self = parent 并使用self.self.getatt()

标签: python class merge


【解决方案1】:

我在调用不同的构造函数时遇到了麻烦,在这种情况下使用 super 不一定有意义,我选择手动继承并调用当前对象上的每个构造函数:

class Foo(object):
    def __init__(self, foonum):
        super(Foo, self).__init__()
        self.foonum = foonum


class Bar(object):
    def __init__(self, barnum):
        super(Bar, self).__init__()
        self.barnum = barnum

class DiamondProblem(Foo, Bar):
    # Arg order don't matter, since we call the `__init__`'s ourself.
    def __init__(self, barnum, mynum, foonum):
        Foo.__init__(self, foonum)
        Bar.__init__(self, barnum)

        self.mynum = mynum

【讨论】:

    【解决方案2】:

    最佳答案在 cmets 中,它对我很有用,所以我决定在答案中展示它(感谢 sr2222): 在 Python 中动态声明继承的方法是 type() 内置函数。 以我为例:

    class A(object) :
        def __init__(self, args):
            self.a = 'a'
            self.args = args
    
        def getattA(self):
            return self.a, self.args
    
    class B(object) :
        b = 'b' 
        def __init__(self, args) :
            self.b_init = args
    
        def getattB(self):
            return self.b
    
    C = type('C', (A,B), dict(c='c'))
    
    instc = C('args')
    
    print 'attributes :', instc.a,  instc.args, instc.b, instc.c
    print 'methodes :', instc.getattA(), instc.getattB()
    
    print instc.b_init
    

    代码返回:

    attributes : a args b c
    methodes : ('a', 'args') b
    Traceback (most recent call last):
      File "D:\Documents and settings\Bureau\merge2.py", line 24, in <module>
        print instc.b_init
    AttributeError: 'C' object has no attribute 'b_init'
    

    我的 C 类 inerhite 属性和 A 类和 B 类的方法,我们添加 c 属性。随着 C (instc = C('args')) 的实例化,A 的 init 被调用,但 B 不被调用。

    对我来说非常有用,因为我必须在不同的类上添加一些属性和方法(相同)。

    【讨论】:

    • 我也遇到了这个问题,这非常有用。就我而言,我有父类的不同“可组合”子类。特定子类的添加成为父类的方法。
    【解决方案3】:

    这个怎么样?

    class A:
        def __init__(self):
            self.a = 'a'
    
        def getatt(self):
            return self.a
    
    class B:
        def __init__(self, parent) :
            self.parent = parent
    
        def __getattr__(self, attr):
            return getattr(self.parent, attr)
    
        def getattB(self):
            return self.parent.getatt()
    
    insta = A()
    instb = B(insta)
    
    print instb.getattB()
    print instb.getatt()
    

    但是A类中的方法不能访问B类中的attr。

    另一种方式:

    import functools
    class A:
        def __init__(self):
            self.a = 'a'
    
        def getatt(self):
            return self.a
    
    class B:
        def __init__(self, parent):
            for attr, val in parent.__dict__.iteritems():
                if attr.startswith("__"): continue
                self.__dict__[attr] = val
            for attr, val in parent.__class__.__dict__.iteritems():
                if attr.startswith("__"): continue
                if not callable(val): continue
                self.__dict__[attr] = functools.partial(val, self)
    
        def getattB(self):
            return self.getatt()
    
    insta = A()
    instb = B(insta)
    
    print instb.__dict__
    print instb.getattB()
    print instb.getatt()
    

    init 慢,但调用快。

    【讨论】:

    • 是的,我认为这是做我想做的事情的唯一方法。我将使用 A 类将属性和方法分配给 B。就像(在 B 类中)self.a = self.parent.a 和 def getatt(self): return self.parent.getatt() 我正在寻找一种更快的方法但是...谢谢
    • 这只是基本上重新实现了一种用于动态继承的新语法,Python 为您提供了一个开始的语法。 insta = A()instb = type('B', (A,), {})。只需在 B 类上声明一个静态方法,该方法采用任意长度的其他类元组并返回 type([new class name], [tuple of classes including B], [members you want to add at run time])。
    • @sr2222,太好了,我试试instb = type("", (object, B, insta.__class__), insta.__dict__)() 可以做得更好。
    • 真的很好。我可以将我的两个类与 C = type('C', (A,B), {}) 合并。而且我在 C 中有 A 和 B 的方法和属性。(只有 A 被初始化)
    • @showns 我不确定使用空字符串作为类名有多安全......此外,如果您只是使用新样式类开头并具有 A和 B 从对象本身继承。无论如何,您确实应该使用新样式的类...而且 insta.__dict__ 也是完全多余的,因为这首先是子类化的重点...
    【解决方案4】:

    我不确定您要做什么,但下面的代码给出了我认为您期望的输出。注意:

    1. a 在 A 中的构造函数之外初始化
    2. B 被声明为 A 的子类

    代码:

    class A:
        a='' #Initialize a
    
        def __init__(self):
            self.a = 'a'
    
        def getatt(self):
            return self.a
    
    class B(A):  #Declare B as subclass
        def __init__(self, parent) :
            self = parent
    
            print self.getatt()
    
        def getattB(self):
            return self.getatt()
    
    insta = A()
    instb = B(insta)
    
    print instb.getattB()
    

    【讨论】:

    • 是的,这是我想做的,但 B 的父级并不总是 A 类而且我不会为每个类创建一个子类。
    • self赋值是不对的,并没有逃逸函数体作用域...
    • 我认为动态设置父类是不可能的。为了让编译器找到超类的属性,必须声明父级。如果B 未声明为A 的子类,您将无法访问a
    • @Po'Lazarus 我知道这一点,但我想给他一个“解决方案”来解决他的问题。我不知道他为什么要做他正在做的事情,所以我不想告诉他他所做的事情是错误的,只是给他一个获得他想要的输出的方法。
    • 可以在 Python 中动态声明继承。如果你有一个接收一个或多个类型参数的构建器函数,你可以将它们传递给 type() 并在运行时创建一个从这些类型子类化的新类型。
    【解决方案5】:

    由于 B 不是 A 的子类,因此 B 中没有通往 A 中 getatt() 的路径

    【讨论】:

      【解决方案6】:

      我想我有一个更简单的方法

      class fruit1:
        def __init__(self):
          self.name = "apple"
          self.color = "blue"
      
      class fruit2:
        def __init__(self):
          self.name = "banana"
          self.size = 100
      
      def merge(ob1, ob2):
          ob1.__dict__.update(ob2.__dict__)
          return ob1
      
      f1 = fruit1()
      f2 = fruit2()
      
      fruit = merge(f1, f2)
      print("name:",fruit.name," color:",fruit.color, " size:",fruit.size)
      #output: name: banana  color: blue  size: 100
      

      【讨论】:

        【解决方案7】:

        下面的Helper函数进行数据类实例的合并,属性orders来源于*argsorder:

        from dataclasses import dataclass
        
        @dataclass
        class A:
            foo: str
            bar: str
        
        
        def merge_dataclasses(*args):
            if len({e.__class__.__name__ for e in args}) > 1:
                raise NotImplementedError('Merge of non-homogeneous entries no allowed.')
            data = {}
            for entry in args[::-1]:
                data.update(vars(entry))
            return entry.__class__(**data)
        
        print(merge_dataclasses(A(foo='f', bar='bar'), A(foo='b_foo', bar='b_bar')))
        

        【讨论】:

          猜你喜欢
          • 2015-10-15
          • 2015-02-08
          • 2019-03-27
          • 1970-01-01
          • 2021-08-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-15
          相关资源
          最近更新 更多