【问题标题】:Multiple inheritance: The derived class gets attributes from one base class only?多重继承:派生类仅从一个基类获取属性?
【发布时间】:2018-10-23 23:20:58
【问题描述】:

我试图在 Python 中学习多重继承的概念。考虑一个派生自两个类Base1Base2 的类DervDerv 仅从第一个基类继承成员:

class Base1:
    def __init__(self):
        self.x=10

class Base2:
    def __init__(self):
        self.y=10

class Derv (Base1, Base2):
    pass

d = Derv()
print (d.__dict__)

结果是{ 'x' : 10 },颠倒继承顺序只会得到{ 'y' : 10 }

派生类不应该继承两个基类的属性吗?

【问题讨论】:

  • 提示:xy 不是类属性。但是,__init__ 是。
  • 那么 x 和 y 是什么
  • 准确地说xy是实例属性?
  • 并且实例属性不被继承?我真的不明白,来自混合的 C++、Java、C# 背景,这有什么关系。能不能给个详细的答案...
  • 只是:继承是类可以与其他类而不是它们的实例之间的关系——尽管后者的行为可能会受到其类所具有的任何此类连接的影响。

标签: python multiple-inheritance


【解决方案1】:

我不完全明白为什么会这样,但我可以告诉你如何解决它:

出于某种原因,Python 仅调用其父对象之一的 __init__ 方法。但是,这可以解决您的问题:

class Base1:
     def __init__(self):
         super().__init__()
         print('b1')
         self.x=10

 class Base2:
     def __init__(self):
         super().__init__() # This line isn't needed. Still not sure why
         print('b2')
         self.y=10

 class Derv (Base1, Base2):

     def __init__(self):
         super().__init__()

 d = Derv()
 print (d.__dict__)


'b2'
'b1'
{'y': 10, 'x': 10}

更新,添加打印语句实际上揭示了一些情况。例如,

class Base1:
     def __init__(self):
         print('Before Base1 call to super()')
         super().__init__()
         print('b1')
         self.x=10

 class Base2:
     def __init__(self):
         print('Before Base2 call to super()')
         super().__init__() # No remaining super classes to call
         print('b2')
         self.y=10

 class Derv (Base1, Base2):

     def __init__(self):
         super().__init__()

 d = Derv()
 print (d.__dict__)

'Before Base1 call to super()' # Just before the call to super
'Before Base2 call to super()' # Just before call to super (but there are no more super classes)
'b2' # Calls the remaining super's __init__
'b1' # Finishes Base1 __init__
{'y': 10, 'x': 10}

【讨论】:

  • 在所有基类上调用 super().__init__() 调用 init 也是如此。 super() 究竟返回了什么?
  • 我认为它只是调用了第一个super的__init__方法
  • 伙计们,建议您阅读大师 Raymond Hettinger 的 Python’s super() considered super!
【解决方案2】:

当一个类从多个超类继承,并且有 2 个或多个冲突方法时,将调用第一个列出的类中的方法。因为Base1Base2 都定义了__init__,所以调用了第一个列出的类中__init__ 的版本,因此没有定义这两个属性。

【讨论】:

  • 太好了,我知道这是因为 MRO。但是对 super().__init__ 的调用如何从两个基类调用 init。 super() 返回什么!
  • 使用语法SuperclassName.__init__(self, ...)更可靠,它调用指定超类的超类构造函数。
【解决方案3】:

你可以像这样创建具有类属性的对象:

class Base1:
    x=10

class Base2:
    y=10

那么您的 Derv 类确实会继承这两个属性。

class Derv (Base1, Base2):
    pass

d = Derv()
print(d.x, d.y)  # prints: 10, 10

__init__ 方法在您创建对象的实例时调用(即d = Derv())。一个对象只能有一个给定方法的版本,因此您的 Derv 类只继承第一个。

【讨论】:

  • 我在阅读收到的答案后明白这一点。但是,现在的困惑是 super().__init__() 如何从两个基类调用 init。
  • 一般super()调用父类的__init__方法。在多重继承的情况下,它应该调用定义此方法的第一个父级(从左到右列出)的__init__ 方法。见:stackoverflow.com/questions/3277367/…
【解决方案4】:

Python docs 对此进行了更好的解释。

对于大多数目的,在最简单的情况下,您可以考虑搜索 对于从父类继承为深度优先的属性, 从左到右,不在同一个类中搜索两次 层次结构中的重叠。因此,如果在 DerivedClassName,在 Base1 中搜索,然后(递归)在 Base1 的基类,如果在那里没有找到,那就是 在 Base2 中搜索,依此类推。

那么,__init__ 是在你的左边班级中首先找到的,它不会寻找其他人。正如其他用户所解释的,super() 应该用于让 Python 知道如何查找其他 __init__ 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-22
    • 1970-01-01
    • 2022-12-11
    • 2012-12-10
    相关资源
    最近更新 更多