Search code examples
pythonooptypeerrormultiple-inheritance

Multiple Inheritance in Python - TypeError "missing 1 required positional argument"


I'm only really getting to grips with OOP and I have been looking at multiple inheritance. The problem I have, is when I instantiate the class Hybrid, which inherits from more than one class, I get the following error:

Traceback (most recent call last):
  File "multi_inherit.py", line 44, in <module>
    hawk = Hybrid('hawk', 200, 650)
  File "multi_inherit.py", line 35, in __init__
    Archer.__init__(self, name, arrows)
  File "multi_inherit.py", line 13, in __init__
    super().__init__(name)
TypeError: Wizard.__init__() missing 1 required positional argument: 'power'

The other classes work absolutely fine.

class User:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Welcome, {self.name.title()}."


class Archer(User):
    def __init__(self, name, arrows):
        super().__init__(name)
        self.arrows = arrows

    def shoot_arrow(self):
        self.arrows -= 1
        return "You shot an arrow!"

    def arrows_left(self):
        return f"You have {self.arrows} arrows remaining."


class Wizard(User):
    def __init__(self, name, power):
        super().__init__(name)
        self.power = power

    def cast_spell(self):
        return f"You cast a spell with a power of {self.power}"


class Hybrid(Archer, Wizard):
    def __init__(self, name, arrows, power):
        Archer.__init__(self, name, arrows)
        Wizard.__init__(self, name, power)

    def powerful(self):
        return "Hybrids are super powerful!"


merlin = Wizard('merlin', 1000)
robin = Archer('robin', 150)
hawk = Hybrid('hawk', 200, 650)

print(merlin.greet())
print(merlin.cast_spell())

print(robin.arrows_left())
print(robin.shoot_arrow())
print(robin.arrows_left())

Solution

  • The classes as you have them now aren't great for use in multiple inheritance. When using multiple inheritance I prefer having constructors that agree on the same contract (use the same arguments) or no arguments at all. Here powers and arrows differ which makes calling each constructor awkward.

    IMO A better way to design this class would be mixins. The mixins would have no constructors and depend on particular values being present in the classes which extend them.

    Example Mixins:

    class UserMixin:
        name: str
    
        def greet(self):
            return f"Welcome, {self.name.title()}."
    
    
    class ArcherMixin(UserMixin):
        arrows: int
    
        def shoot_arrow(self):
            self.arrows -= 1
            return "You shot an arrow!"
    
        def arrows_left(self):
            return f"You have {self.arrows} arrows remaining."
    
    
    class WizardMixin(UserMixin):
        power: int
    
        def cast_spell(self):
            return f"You cast a spell with a power of {self.power}"
    

    Example Implementations:

    class User(UserMixin):
        def __init__(self, name):
            self.name = name
    
    
    class Archer(ArcherMixin):
        def __init__(self, name, arrows):
            self.name = name
            self.arrows = arrows
    
    
    class Wizard(WizardMixin):
        def __init__(self, name, power):
            self.name = name
            self.power = power
    
    
    class Hybrid(ArcherMixin, WizardMixin):
        def __init__(self, name, arrows, power):
            self.name = name
            self.arrows = arrows
            self.power = power
    
        def powerful(self):
            return "Hybrids are super powerful!"
    

    Example usage:

    merlin = Wizard('merlin', 1000)
    robin = Archer('robin', 150)
    hawk = Hybrid('hawk', 200, 650)
    
    print(merlin.greet())
    print(merlin.cast_spell())
    
    print(robin.greet())
    print(robin.arrows_left())
    print(robin.shoot_arrow())
    print(robin.arrows_left())
    
    print(hawk.greet())
    print(hawk.cast_spell())
    print(hawk.arrows_left())
    print(hawk.shoot_arrow())
    print(hawk.arrows_left())
    
    Welcome, Merlin.
    You cast a spell with a power of 1000
    Welcome, Robin.
    You have 150 arrows remaining.
    You shot an arrow!
    You have 149 arrows remaining.
    Welcome, Hawk.
    You cast a spell with a power of 650
    You have 200 arrows remaining.
    You shot an arrow!
    You have 199 arrows remaining.