Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Multiple Inheritance in Python

Python supports inheritance from multiple classes. In this lesson, you’ll see:

  • How multiple inheritance works
  • How to use super() to call methods inherited from multiple parents
  • What complexities derive from multiple inheritance
  • How to write a mixin, which is a common use of multiple inheritance

A class can inherit from multiple parents. For example, you could build a class representing a 3D shape by inheriting from two 2D shapes:

Language: Python
class RightPyramid(Triangle, Square):
    def __init__(self, base, slant_height):
        self.base = base
        self.slant_height = slant_height

    def what_am_i(self):
        return 'RightPyramid'

The Method Resolution Order (MRO) determines where Python looks for a method when there is a hierarchy of classes. Using super() accesses the next class in the MRO:

Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Already a member? Sign-In

Locked learning resources

The full lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Already a member? Sign-In

00:00 This is the third of three lessons on inheritance in Python and the use of super() to access methods in parent hierarchy. In this lesson, I’ll be talking about multiple inheritance.

00:12 Multiple inheritance is the process of inheriting from multiple classes into your new base class. In order to do that, I want to add a new base shape called Triangle. Once again, I’m adding this to shapes.py.

00:25 There’s nothing in Triangle you haven’t seen before. The base and the height of the triangle are passed in in the constructor. I’ve added an .area() method, which has the area formula for a triangle—half times the base times the height—and once again, I’ve got a .what_am_i() method so I can show you how the inheritance mechanisms work.

00:44 So, let’s inherit from Triangle. The RightPyramid inherits from Triangle and Squaremultiple inheritance. In case your geometry is as rusty as mine, a right pyramid is one with a square base and four triangles sloping up to its point. In order to define a RightPyramid, you need a length for the base and a slant_height.

01:05 The slant height is the height of the triangle that is slanted to make the side of the pyramid. Like our other shapes, I’ve also defined a .what_am_i() so we can look at the relationships.

01:14 Let’s look at the REPL. Import the RightPyramid,

01:19 create the rightpyramid, and let’s look at the .what_am_i(). super() returns the parent, but in the case of multiple inheritance, it returns the first parent. Because Triangle was defined before Square in the list of the RightPyramid, the super() method calls the .what_am_i() of the Triangle.

01:39 The .__class__ attribute of the RightPyramid, not surprisingly, says RightPyramid, and then you may recall the .__class__ attribute also has a .__bases__ attribute that can show you the inheritance. In the case of RightPyramid, we’re inheriting from Triangle and Square.

01:55 One more thing that I’d like to show you is the .__mro__. This is the method resolution order. This is the order in which Python looks through the inheritance structure.

02:07 In this case, it starts out looking at the RightPyramid, then goes up the inheritance structure to look at the Triangle. Triangle has no parents, so it moves on to the next thing in the multiple inheritance hierarchy, which is the Square. Square’s parent is the Rectangle. And then finally, because Rectangle has no parents, it reaches the top, the <class 'object'>.

02:27 All objects in Python inherit from the object object. You may recall from the first lesson, the results of the dir() function had a lot of double underscore (__) methods in it.

02:37 Those double underscore methods are all defined inside of this object object, which everything in Python inherits from. The method resolution order dictates to Python how to look up a named method when it is called. In single inheritance, this is usually fairly simple.

02:53 It is the path of the inheritance hierarchy. In multiple inheritance, this can get complicated. It’s quite possible that your inheriting classes could have methods of the same name. The MRO dictates which of them gets called. These name clashes can cause problems and can make your code a little confusing.

03:10 There’s different ways of handling it. First off, you could rewrite your code so there are no name clashes. Take our .area() method on Square and Triangle, and rename them to be the .square_area() and the .triangle_area(). Now name resolution isn’t necessary, because the name is unique to the class.

03:27 You can use the inheritance declaration itself to specify the order in which things are looked up. This is where MRO shines. You have to be careful though, because the result between RightPyramid, Triangle, then Square versus RightPyramid, Square, Triangle will change which .area() method gets called. Finally, you can also specify the call itself directly. Using the class object, you can call the .area() method and pass in the object itself.

03:56 This is the most explicit way. If someone else is reading your code, they won’t have to remember what the MRO is; they’ll know you’re using the Square.area() method. To illustrate how the MRO works with multiple inheritance, I’ve created a new file called chain.py. Inside of it, I’m creating five different classes, A, B, X, Forward, and Backward.

04:16 Each one of the classes simply has a constructor that prints out that you’re inside of its constructor and then calls the super().__init__(). At first blush, this might seem weird. If you look at the constructor for A, there is no parent to A, but we’re still calling super().

04:32 You’ll see why in a second. Let’s pull out our trusty REPL and create a couple of objects. First off, forward.

04:40 The first thing Forward() does is call the Forward constructor. The Forward constructor prints 'Forward' and then calls super(). The super of Forward is B.

04:51 So next, we see B. Going into the B class, the super() for this is called, the super of B is A, so 'A' gets printed.

05:00 Then we go into A, and super() of A is called. There is no super for A. Up until now, I’ve always talked about the parent. In single inheritance, this is true. In multiple inheritance, it’s a little bit of a white lie.

05:14 What is actually happening is the next object in the MRO is what is called. So in this case, the super for A is X. If you go back to Forward, Forward inherits from B, then inherits from X.

05:28 So B chains to A, A chains to X when super() is called. To contrast this, let’s look at Backward. Backward’s constructor called super(), that’s X. X also has no parent, but super of X will be the next object in the MRO, which in this case is B. B() calls super(), that inherits from A.

05:51 A() calls super(), and we’re done the chain. As you can see, specifying the order of inheritance of B and then X, or X versus B changes what methods get called in what order. Let’s take the complexity of this chaining and apply it to our shapes code. First off, I’ve got my RightPyramid. Notice that I’ve taken the inheritance order and changed it from before.

06:16 Currently, the RightPyramid is inheriting from Square first and then Triangle. In addition to the potential challenge of method names clashing in inheritance, you also have the problem of how to actually construct them.

06:28 In the RightPyramid you have a base and a slant_height, but it’s based on a Square and a Triangle, whose constructor attributes aren’t base and slant_height—they’re length and height. This can cause problems when you start to inherit. One way around this is to use kwargs (keyword args).

06:45 If you haven’t seen this before, Python supports the ability to pass in a dictionary with the double asterisk (**) in front of it. This dictionary forms name-value pairs as arguments for a method.

06:57 This allows you to pass in attributes that, in this case, RightPyramid is going to ignore.

07:03 I’ve taken the base and the slant_height and assigned them as before, added a "height" and "length" keyword argument, and then I pass it into super().

07:12 What that means is when the super() gets called for the constructor for Square, you now have all of the arguments there: base, slant_height, height, and length. Square() can take the length that it needs and pass that into its own super(). base, slant_height, and height are all still there inside of kwargs, but Square doesn’t use them. Square() calls its own super(). Rectangle pulls out the length and width that it’s interested in inside of kwargs, calls its own super()which of course, there’s no base class for Rectangle, so now we’re going into RightPyramid’s inheritance MRO, calling the Triangle constructor, and the Triangle() gets the base and the height.

07:53 This allows us to have all of the attributes passed in for all of these objects. This works, but it isn’t the easiest code to read. Multiple inheritance solves certain kinds of problems, but it can also make code rather difficult to follow along.

08:08 You need to be careful how you use this tool. There are often ways of constructing the code that are easier to read that achieve the same end result. The complexities of multiple inheritance make programmers wary sometimes about when to use it.

08:22 One of the easiest ways of making sure you don’t have problems is to create classes that don’t have name clashes and are as independent as possible.

08:30 A pattern that’s very common in a lot of frameworks like Django and Flask is something called a mixin. A mixin is an independent class that gets pulled in in the inheritance hierarchy but is not going to impact anything that inherits it. An example of this is the SurfaceAreaMixin.

08:48 The SurfaceAreaMixin provides a single method, which is .surface_area(), and has no expectation about construction. All it requires is that somewhere in the class that is using it, there’s an attribute called .surfaces. Here’s the RightPyramid modified to use the SurfaceAreaMixin.

09:05 SurfaceAreaMixin is inherited into the RightPyramid and .surfaces is defined—a list of the different surfaces for the RightPyramid.

09:15 The .surface_area() method can now be called calculating the surface area without the SurfaceAreaMixin having to understand what the shape is that is being inherited from. Here’s another example with the Cube. Same idea—SurfaceAreaMixin is inherited from, and the six surfaces of the Cube are defined inside of .surfaces.

09:37 The independence of the SurfaceAreaMixin means there aren’t any name clash complexities, but you still have the power of inheritance.

09:46 Let’s see this in the REPL. We construct our Cube, call our .surface_area(), and get our result.

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on Feb. 8, 2021

Hi @rikhuygen,

Yep, this is one of the challenges of trying to teach mixins and object oriented lessons. The really good uses of this kind of tech tend to require more background information.

The whole “using shapes” thing to explain OO inheritance is tried and true but has its limitations. Even if I was going to write a graphic interface that used shapes, I likely wouldn’t go full OO, or if I did, the base class would be much lighter than is used in typical explanations.

The best place for things like Mixins are where you have a lot of independence and are trying to add a feature that isn’t quite specific.

For example, I recently wrote something in Django that required a special kind of identifier. The identifier was a little more complicated than just a field and needed some methods. All of this was put inside of a mixin so that any Django object that needed this kind of identifier would just mix that in. The problem with teaching with this kind of example is then you have to explain what Django is, and how it models objects and how that relates to fields and a whole bunch of other stuff.

To your specific question, the amount of code saved by using this mixin probably doesn’t warrant the hiding of the implementation that happens the way it was done. In the real world I probably wouldn’t have coded it that way, but then I wouldn’t have had a mixin example.

Avatar image for rikhuygen

rikhuygen on Nov. 9, 2021

Fair enough, thanks for your answer. (sorry for my late reply)

Avatar image for Alex

Alex on Jan. 6, 2023

Hi, Christoph, i’ve enjoyed the tutorial. Could you please answer to my following 2 questions (kind of similar to @Saul, but not entirely i guess):

–1. Is a bad practice to create instance variables(instead of passing it through kwargs) in the init of the Child class, if they are consumed only in the Parent classes, like in the first part of the following example:

class RightPyramid( Triangle,Square):
    def __init__(self, base, slant_height):#no kwargs
        self.base = base
        self.slant_height = slant_height
        self.width = self.base #added width in self
        self.length = self.width #added length in self
        super().__init__(base=self.base,height = self.slant_height) #no kwargs


class RightPyramid( Triangle,Square):
    def __init__(self, base, slant_height,**kwargs): #added kwargs
        self.base = base
        self.slant_height = slant_height
        kwargs['length'] = self.base #added k/v for length
        super().__init__(base=self.base,height = self.slant_height,**kwargs) #added kwargs

–2. What would be the difference between passing the variable in kwargs or passing it separately through the keyword argument?

class RightPyramid( Triangle,Square):
    def __init__(self, base, slant_height,**kwargs):
        self.base = base
        self.slant_height = slant_height
        #kwargs['length'] = self.base # pass by kwargs
        super().__init__(base=self.base,height = self.slant_height,length=self.base,**kwargs) # pass by extra keyword
Avatar image for Alex

Alex on Jan. 6, 2023

Sorry, just a later note, i guess having mixings is a nice feature, but one disadvantage(please correct me if wrong) is that it is polluting the instance with the unrelated instance variables of the RightPyramid class for example:

class RightPyramid(Square, Triangle, SurfaceAreaMixin):
    def __init__(self, base, slant_height):
        self.base = base
        self.slant_height = slant_height
        self.height = slant_height #not needed directly by the class
        self.length = base #not needed directly by the class
        self.width = base #not needed directly by the class

        self.surfaces = [Square, Triangle, Triangle, Triangle, Triangle]
Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on Jan. 7, 2023

Hi Alex,

Use of **kwargs is the more general case, and I might do it if I’m not trying to expose just what the parent is doing. It is kind of a catch-all. If you’re building structures where you’re inheriting directly, there is nothing wrong with using named arguments instead.

One advantage of **kwargs is pass-through to the next level. The best example I can think of is on database objects in Django: they have a save() method that takes a number of parameters. You can override a save() in a child class and only use the arguments that you need, but if some other coder comes along and needs the arguments you didn’t declare, they’re stuck. So best practice is to use **kwargs for those things you’re not using and pass them all to the parent call.

It really just comes down to a design decision though.

As for your question about polluted classes, yeah, in this case that is an unfortunate side-effect. Shapes are a good analogy for teaching inheritance concepts, but are just an analogy. The best examples of mixins usually require deeper understanding of other systems than is helpful when teaching.

My most commonly used mixin is again in the Django space. I like to have date/time stamps on all database tables that track when a row was created and last updated. It can be useful when debugging production problems. As such, the mixin has these fields in it and just gets added to any data class I create. As these fields and any associated methods are independent of the classes mixing them in, there is no pollution problem. Teaching with this example is problematic though, because you can’t run the code unless you’ve got all the Django stuff setup, and so you’re left with having to use mediocre analogies.

With a lot of this stuff it comes down to practice. Even with practice there are plenty of times where I got bit by a design decision I’ve made and then have to come back in and change it to something else later. Welcome to the fun of coding :)

Avatar image for Andras

Andras on March 22, 2024

Hi Chris,

I enjoy your videos very much. At around 8:00, when you show all classes and their __init__ methods, RightPyramid is calling Square’s __init__ with a base keyword, which it doesn’t have. Shouldn’t the last line read:

super().__init__(length=base, **kwargs) ?

Thanks

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on March 23, 2024

Hi Andras,

Multiple inheritance is tricky.

class RightPyramid(Square, Triangle):
    def __init__(self, base, slant_height, **kwargs):
        self.base = base
        self.slant_height = slant_height
        kwargs["height"] = slant_height
        kwargs["length"] = base
        super().__init__(base=base, **kwargs)

The RightPyramid is inheriting from both Square and Triangle. In this code I’m using base, height, and length in super().__init(). With the base being specified explicitly, and the height and length in kwargs.

You’re right that the Square has no base, but because the arguments are named, and because Square accepts **kwargs: base gets passed in but ignored.

The length value that Square accepts is set in the kwargs dictionary inside RightPyramid, and in fact is a copy of base.

If Square and Triangle did not have **kwargs in their constructors, this code would not work. Since they do have **kwargs, you can pass anything you want in as a named argument, but their respective __init__ code only uses the named arguments they’re interested in. Everything else gets ignored.

Hope this helped.

Avatar image for Andras

Andras on March 25, 2024

Hi Chris,

Thanks for the answer. I think most of my confusion came from how **kwargs are handled in general; it was not directly related to multiple inheritance. My previous proposal would have resulted in TypeError: Square.__init__() got multiple values for argument 'length' if I ran it, which I did not (shame on me). Now that I understand **kwargs better, let me ask one more minor thing: when you write the RightPyramid constructor, you know the direct parent is Square, so you could pass lenght=base directly and pass on everything else in kwargs that other parents (further up the chain) will need, like this:

class RightPyramid(Square, Triangle):
    def __init__(self, base, slant_height, **kwargs):
        self.base = base
        self.slant_height = slant_height
        kwargs["height"] = slant_height
        kwargs["base"] = base  # note "base" key!!!

        super().__init__(length=base, **kwargs)  # keyword length=base
        #super().__init__(base, **kwargs)  # positional arg would work too

Was there any specific reason why you “hid” length in kwargs and passed base explicitly? With your approach kwargs inside Square.__init__ becomes a different dict than the one you passed in. This is not a problem, I understand it now. This is the magic Python gives us automatically with kwargs functionality (select what’s needed, ignore everything else). Just asking if there was any trick or design pattern you followed that I should keep in mind too when writing similar code.

regards, Andras

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on March 26, 2024

Hi Andras,

Glad you figured it out. Given the course is four years old, I’m not sure I can say exactly what was going through my head when I coded it.

I could argue the more generic use of keyword values is slightly safer in the case where you end up changing the inheritance order, but I’m not sure how important that is. If I was doing a code review, I’m not sure I would be too worried about this case.

The secret reality of all this stuff is that I very seldom use multiple inheritance. It is far more common in other languages (and sometimes far more necessary). Although it is a core computer science concept, and a useful tool in your toolbag, it is only one of a choice of approaches. When I do use multiple inheritance, it tends to be for mixins, which by definition have argument-less constructors, so this particular case we’re discussing doesn’t come up.

If you enjoy the OO aspect of Python and want to understand a bit more, one of the more recent courses does an in depth dive. It has three parts, covering:

There is some overlap with this course, but far more depth. The third course talks about some of the rules of object-oriented design and touches on some of the whens/whys of usage.

Happy coding!

Avatar image for Andras

Andras on March 26, 2024

Thank you very much, Chris! I just finished that three-part course few days ago. I enjoyed it very much, especially the last part on design. I have many years C++ experience and I feel like I am still looking for the same design principles in Python that I used to work with in C++. It would be nice if you could put together some courses comparing Python vs C++ or Java, and explain what’s the pythonic approach to the most common problems in OOP that are usually tackled with design patterns in C++/Java (singleton, visitor pattern, etc.)

Keep up the good work!

cheers, Andras

Become a Member to join the conversation.