Multiple Inheritance Python

I was doing some planning for a python module today and got tripped up when I started to consider some of the class relationships. Specifically, there are elements common to some classes, which would have very repetitive tests. It made sense to try and factor them out as their own class. But then, how to incorporate those classes into a child class. My first thought is inheritance, but in the past I have been cautioned against it. The image below helps demonstrate the issue.

Diamond Problem

If the same method, eat(), is defined in some of these, excluding the child (twix), it is hard to determine what twix.eat() will return. twix.eat() can of course be overridden in twix, but it gets messy fairly quickly if you want to compose eat() methods from leftTwix, rightTwix, and chocolate.

class chocolate:
    def eat(self):
        print("Eating chocolate")

class leftTwix(chocolate):
    def eat(self):
        print("Eating leftTwix")

class rightTwix(chocolate):
    def eat(self):
        print("Eating rightTwix")

class twix(leftTwix,rightTwix):
    pass


x = twix()
x.eat()
Eating leftTwix

The super() method handles this by applying "C3 superclass linearisation." Ah, smells like gibberish. It converts the tree in the image above to a linear order.

class chocolate:
    def eat(self):
        print("Eating chocolate")

class leftTwix(chocolate):
    def eat(self):
        print("Eating leftTwix")
        super().eat()

class rightTwix(chocolate):
    def eat(self):
        print("Eating rightTwix")
        super().eat()

class twix(leftTwix,rightTwix):
    def eat(self):
        print("Eating Twix")
        super().eat()


x = twix()
x.eat()
Eating Twix
Eating leftTwix
Eating rightTwix
Eating chocolate

For the sake of example, the typical use is to initialize the parent class with super.

class chocolate:
    def __init__(self):
        print("Init Eating chocolate")

class leftTwix(chocolate):
    def __init__(self):
        print("Init Eating leftTwix")
        super().__init__()

class rightTwix(chocolate):
    def __init__(self):
        print("Init Eating rightTwix")
        super().__init__()

class twix(leftTwix,rightTwix):
    def __init__(self):
        print("Init Eating Twix")
        super().__init__()


x = twix()
Init Eating Twix
Init Eating leftTwix
Init Eating rightTwix
Init Eating chocolate

It is typical to use inheritance when initializing the child classes.

The points are:

  1. super() allows for consistent method resolution order
  2. prefer composition, otherwise keep inheritance simple

So for 2, my heuristic is to allow for simple multiple inheritance within modules where needed, not between them. Between modules, I will stick to composition. I'm still fuzzy on what "where needed" means. But it just dawned on me that the current case wouldn't even work with inheritance as I may need multiple instances of the parents in each child! So it's composition today.

Glad to hear of other people's best practice for class inheritance. "Don't" is common in the prevailing wisdom, which is absurd, it clearly has its place.

References:
http://www.python-course.eu/python3_multiple_inheritance.php

Go Top
comments powered by Disqus