Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Cannot create a consistent method resolution order" inheriting from another child class

I am revising OOP in Python and tried inheriting attributes from another child class but I couldn't figure out how or if it is possible. Here is what I have so far:

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    

The error I receive is:

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
like image 785
j_yerbe Avatar asked Oct 19 '25 02:10

j_yerbe


1 Answers

The method resolution order (MRO) is defined by the C3 linearization algorithm, which sounds complicated but it really boils down to this: the class, its parents, their parents, etc need to be placed in a list subject to two conditions:

  1. Each class appears before its parent(s)
  2. If a class inherits from more than one class, its parents appear in the same order as they do in class statement. That is, given class A(B, C, D), the MRO for A will have B before C, which will be before D. (A, of course, appears before all 3)

You should be able to see the problem: by this algorithm, the MRO for BetaTester has to include Developer before Employer according to the first rule, but Employer has to come before Developer according to the second rule. In this case, you can simply swap the two to fix the problem, but there's never any reason to inherit from a class A and another class the inherits from A. Just drop A altogether.

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

To make sure each class's __init__ method is called, use super to make sure each __init__ calls the next one in the chain. The most important rule here is to make sure that if a class adds arguments to __init__, it has to make sure not to pass them on to the next __init__. At the same time, it has to accept arbitrary keywords arguments and be sure to pass them on. Keyword arguments make it simpler to focus on the arguments you need to deal with, and just pass on the ones you don't.

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")
like image 92
chepner Avatar answered Oct 21 '25 15:10

chepner