A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://realpython.com/lessons/multiple-inheritance-python/ below:

Multiple Inheritance in Python (Video) – Real Python

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

Unlock This Lesson

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:

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

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:

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

Unlock This Lesson

Already a member? Sign-In

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.

Course Contents


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4