Multiple Inheritance - Multiple Parents
00:00 In the previous lesson, I introduced you to inheritance and multi-level hierarchies. In this lesson, Iâll show you how a class could have multiple parents.
00:10 Python supports multiple inheritance, meaning a class can be a child of many parents. When you do this, you get a union of all the things in every parent you inherit from.
00:20
Letâs go look at some code. Okay, more classes about vehicles and including flying vehicles. You just sort of run out of examples after a while. At the top here, I have a parent class having .make and .model attributes and .start() and .stop() methods. Let me scroll down a bit.
00:41
Here is a Car. The Car is a Vehicle, and as it doesnât override anything, it gains the .__init__(), attributes, and .start() and .stop() methods from Vehicle.
00:52
It then declares its own additional method called .drive(). Scrolling a bit more â¦
01:01
AirCraft is another Vehicle. Similar to Car, it gets its parentâs members and adds its own method. And here itâs time to enter the future: a flying car.
01:12
The syntax for inheriting from multiple classes is to comma-separate the list of classes in the declaration. The FlyingCar is both a Car and an AirCraft, which of course also makes it a Vehicle through its grandparents.
01:26 Letâs create some objects.
01:39
Iâve created a FlyingCar and then called .start(). Remember that method was declared in Vehicle, the grandparent, and
01:51
as FlyingCar is a Car, it can drive.
01:56
And because itâs an AirCraft, it can fly. The same rules around super() apply here as well. Everything from the previous lesson can be used in conjunction with multiple class inheritance.
02:10
Multiple inheritance introduces a problem though you can get conflicts. Consider this diagram representing the classes you just saw. What happens if I extend the .start() method in Car, changing its behavior?
02:23
Now I have what is called the diamond problem, named after the shape of the class diagram. Which .start() does FlyingCar get? The one from Car or the one from AirCraft, which AirCraft inherited from the grandparent, Vehicle?
02:39 Different languages deal with this problem differently. Letâs talk about how Python does it. Pythonâs answer is called the method resolution order, or MRO to its friends. When you look for a member on a class or object, Python looks for it in the following order. First, it looks at the current class, then at the leftmost superclass.
03:02
Thatâs the first one listed in the inheritance declaration. That was Car in my example. After that, it moves in order along the inheritance declaration. So after Car, it would check in AirCraft. If it still hasnât found what you asked for, it then moves up to the grandparents, following the same inheritance declaration order until finally it gets to the ancestor of all objects. In Python, the object class. That nameâs not confusing.
03:31
If you ever wondered where .__init__(), .__str__(), and similar methods are defined if you donât define them in your class, well, theyâre in the object class.
03:40 All classes in Python inherit from it automatically. So, the short version of the MRO: the order of the class declaration statement is the order Python looks for things. Letâs go resolve some method orders.
03:55
Pardon the generic class names here, but sometimes an abstract concept makes it clearer. This file has four classes: A, B, C, and D. A is the grandparent, B inherits from A, C inherits from A, and D inherits from B and C. A, B, and C all have a method creatively named .method(). As D doesnât override anything.
04:20
The MRO is what determines what happens. Before I show the result, take a second and determine what will happen when I call .method() on the D object. Okay, letâs see if you were right.
04:34
Importing D ⦠instantiating ⦠and calling DâS .method() method. As D doesnât override the .method() method, the MRO gets used.
04:47
The first class that D inherits from is B, so Bâs method is what was resolved. Python even includes a handy attribute that shows you the MRO for a class in case you arenât sure.
05:01
.__mr0__ shows D, B, C, A, and then the ancestor of all: object. If you ever run into a tricky inheritance problem and youâre not sure where something is coming from, .__mro__ can be helpful.
05:17
A mixin is a special name for a class that doesnât have any attributes. You inherit from a mixin in order to add its methods to your class. This often serves the same purpose as that oh-so-common util.py file everybody has where you put miscellaneous functions.
05:34 Itâs the object-oriented version of the same. You build a mixin with generally useful methods, then include it in any class where you need those methods.
05:45 Mixins are never instantiated directly, and youâll see them a lot in frameworks. For example, in Django, you might use one that adds database query features, and youâd mix it in with your database models.
05:59
A quick tangent before I show you a mixin. Python stores the writable members of classes and objects in an internal dictionary. This dictionary is named .__dict__.
06:11
The built-in vars() function displays the contents of that dictionary on your object. I kind of glossed over something quickly at the top of this slide.
06:20 Just what do I mean by writable? Well, things that you can change. For an instance object, that is its attributes. For a class, thatâs pretty much everything.
06:33 The next couple sentences are a bit of a deep dive behind the scenes, and you really donât have to worry about it when youâre writing classes, but methods are functions bound to a class using a reference.
06:45
When you invoke a method on an instance, itâs invoking the corresponding bound function on the class, passing in the instance. Because itâs an object, you can actually modify the reference on the class and point it to a different function. You really shouldnât unless youâre trying to do something fancy, but thatâs actually how it works. Why do I bring all this up? Well, if you look at the writable things on a class, that includes the methods as well as any class attributes. For the purpose of the mixin Iâm about to show you, the important thing you have to remember is that .__dict__ contains the attributes of an object. Thereâs an exception to all this, but Iâll leave that to a later lesson.
07:29
This file contains a mixin that does JSON serialization. The JSONMixin is a class like any other. It has methods and no attributes. The .to_json() method uses the json moduleâs dumps() function to turn a dictionary into a string containing JSON.
07:47
What dictionary would that be? Well, the .__dict__ contains all the writable members of the object, which are its attributes, which is what I want to serialize.
07:57
And then to use it, the Circle class simply inherits from the mixin. That means the radius and the color attributes can be turned into JSON.
08:08
Likewise, the Rectangle also inherits from the mixin, gaining the same powers. Letâs use this. Importing â¦
08:21
creating a circle. Hereâs Circleâs .__dict__ ⦠and the same stuff but using vars() to get at it.
08:35
Remember when I spoke about that writable stuff? Ah, for giggles, letâs look at the classâs .__dict__.
08:42
This is a bit more complex than at the instance level. It contains two special attributes showing the module and docstring, and the .__init__() method as a function reference. Like I said, more an implementation detail.
08:55 Unless youâre getting really tricky with your classes, you really donât have to worry about this. All right, and what you came to see: the mixin.
09:04
The .to_json() method serializes the attributes of the Circle object, and of course, the reason you do mixins is code reuse. Hereâs the Rectangle â¦
09:24 Coming up, Iâll lift the covers a little bit more and show you some internals of classes.
Become a Member to join the conversation.

waqe on March 28, 2025
Circle.__dict__<– Weeeeeee!! Must study more! (-: