【问题标题】:"Cannot create a consistent method resolution order" inheriting from another child class从另一个子类继承的“无法创建一致的方法解析顺序”
【发布时间】:2019-03-10 19:22:16
【问题描述】:

我正在用 Python 修改 OOP,并尝试从另一个子类继承属性,但我不知道如何或是否有可能。这是我目前所拥有的:

class Employee:
  def __init__(self, first, last, pay):
    self.first = first
    self.last = last
    self.pay = pay
  def increase_pay(self, multiplier):
   self.pay = int(self.pay * multiplier)

class Developer(Employee):
  def __init__(self, first, last, pay, prog_lang):
    Employee.__init__(self, first, last, pay)
    self.prog_lang = prog_lang
    self.email = first.lower() + '.' + last.lower() + '@outlook.com'

class BetaTester(Employee, Developer):
  def __init__(self, first, last, pay, prog_lang, platform):
    self.platform = platform    

我收到的错误是:

Traceback (most recent call last):
  File "main.py", line 33, in <module>
    class BetaTester(Employee, Developer):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Employee, Developer

【问题讨论】:

  • 您遇到了什么具体错误?理想情况下,一个好的minimal reproducible example 应该只包含演示特定问题所需的最短代码——如果任何给定的行对于表达你的观点不是必不可少的,那么该行(以及任何依赖它的行)不应该在这里。
  • @CharlesDuffy 抱歉,我是新来的(第一篇文章)。让我将错误添加到帖子中!
  • 谢谢——这很有帮助! (现有答案可以正确猜测问题所在,但在问题中显示它可以帮助其他人跟进,并使有相同问题的其他人更容易搜索您的问题及其答案)。
  • 顺便说一句,就这仅在 Python 3 中发生的情况而言,我为该语言版本添加了一个标签;并且由于只需要类定义来重现问题,因此我取消了其他所有内容。

标签: python python-3.x oop inheritance


【解决方案1】:

方法解析顺序 (MRO) 由 C3 线性化算法定义,听起来很复杂,但实际上归结为:需要放置类、其父级、它们的父级等在受两个条件约束的列表中:

  1. 每个类都出现在其父类之前
  2. 如果一个类从多个类继承,它的父类出现在同一个类中 就像他们在class 声明中所做的那样。也就是说,给定class A(B, C, D)A 的 MRO 将在 C 之前有 B,而在 D 之前。 (A 当然出现在所有 3 之前)

您应该可以看到问题:通过该算法,根据第一条规则,BetaTester 的 MRO 必须在 Employer 之前包含 Developer,但根据第一条规则,Employer 必须在 Developer 之前到第二条规则。在这种情况下,您可以简单地交换两者来解决问题,但没有任何理由继承自 A 的类 继承自 A 的另一个类。完全放弃A

# Developer is already a descendent of Employee, so BetaTester will be, too
class BetaTester(Developer):
   ...

要确保调用每个类的 __init__ 方法,请使用 super 确保每个 __init__ 调用链中的下一个。这里最重要的规则是确保如果一个类向__init__ 添加参数,它必须确保 将它们传递给下一个__init__。同时,它必须接受任意关键字参数并确定 传递它们。关键字参数使您更容易专注于您需要处理的参数,而只需传递您不需要处理的参数。

class Employee:
    def __init__(self, first, last, pay, **kwargs):
        super().__init__(**kwargs)
        self.first = first
        self.last = last
        self.pay = pay
    def increase_pay(self, multiplier):
        self.pay = int(self.pay * multiplier)


class Developer(Employee):
    def __init__(self, prog_lang, **kwargs):
        super().__init__(**kwargs)
        self.prog_lang = prog_lang
        self.email = "{}.{}@outlook.com".format(self.first.lower(), self.last.lower())


class BetaTester(Developer):
    def __init__(self, platform, **kwargs):
        super().__init__(**kwargs)
        self.platform = platform    


b = BetaTester(first="Bob", last="Jones", pay=90000, prog_lang="Python", platform="Unix")

【讨论】:

    【解决方案2】:

    @MehrdadEP 回答得很好

    我想我应该让它变得简单

    假设有 2 个类 A 和 B

    B 继承自 A

    现在你正在创建一个新的类 C

    如果您从 B 继承,则您已经从 A 继承。无需在 C(A,B) 类中编写

    观察 C(B,A) 不会给你错误 但是 C(A,B) 会

    另一件事是你应该使用Super().__init__() 而不是Employee.__init__() 这在进行混合继承时可能并不方便,因为您必须调用 1 个以上不同的超类,您必须调用 classname1.__init__() , classname2.__init__() 等等

    还要确保如果您想要在 SuperClass 中定义的属性,然后调用 classname.__init__() 以便它们在新类的范围内定义,否则 你会得到一个错误

    例如

    print(b1_1.first) 会报错 解决使用Developer.__init__(self, first, last, pay, prog_lang)

    不要忘记这个Developer.__init__从子类__init__调用中的自我

    【讨论】:

      【解决方案3】:
      1. 在子类的__init__方法中使用super()
      2. 您的BetaTester 继承自EmployeeDeveloper。因为Developer 已经从Employee 继承,Python 现在无法确定首先查找方法的类。您无需在此处命名Developer 的所有基类;只需从那个类继承即可。

      这是你的固定代码:

      class Employee:
        def __init__(self, first, last, pay):
          self.first = first
          self.last = last
          self.pay = pay
      
      
        def increase_pay(self, multiplier):
         self.pay = int(self.pay * multiplier)
      
      emp_1 = Employee('David', 'Jackson', 35000)
      
      print (emp_1.pay)
      
      emp_1.increase_pay(1.2)
      
      print (emp_1.pay)
      
      class Developer(Employee):
        def __init__(self, first, last, pay, prog_lang):
          super().__init__(first, last, pay)
          self.prog_lang = prog_lang
          self.email = first.lower() + '.' + last.lower() + '@outlook.com'
      
      
      
      dev_1 = Developer('James', 'McCarthy', 70000, 'C++',)
      
      print(dev_1.first)
      print(dev_1.email)
      
      
      class BetaTester(Developer):
        def __init__(self,first, last, pay, prog_lang, platform):
          self.platform = platform
      
      bt_1 = BetaTester('Jonas', 'Andersen', 45000, 'C#', 'Mobile')
      
      print(bt_1.platform)
      

      【讨论】:

      • 如果你打算在Developer 中使用super(),你也应该在Employee 中使用它,即使Employee 只继承自object。您不知道从 EmployeeDeveloper 继承的某些 other 类也可能从另一个类继承,而 Employee 不一定是之前 MRO 中的最后一个类object.
      猜你喜欢
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      • 1970-01-01
      • 2020-03-23
      • 1970-01-01
      • 1970-01-01
      • 2019-01-14
      相关资源
      最近更新 更多