When a class is derived from many base classes it is called?
In this article, you’ll explore inheritance and composition in Python. Inheritance and composition are two important concepts in object oriented programming that model the relationship between two classes. They are the building blocks of object oriented design, and they help programmers to write reusable code. Show
By the end of this article, you’ll know how to:
Free Bonus: Click here to get access to a free Python OOP Cheat Sheet that points you to the best tutorials, videos, and books to learn more about Object-Oriented Programming with Python. What Are Inheritance and Composition?Inheritance and composition are two major concepts in object oriented programming that model the relationship between two classes. They drive the design of an application and determine how the application should evolve as new features are added or requirements change. Both of them enable code reuse, but they do it in different ways. Remove adsWhat’s Inheritance?Inheritance models what is called an is a relationship. This means that when you have a 6 class that inherits from a 7 class, you created a relationship where 6 is a specialized version of 7.Inheritance is represented using the Unified Modeling Language or UML in the following way: Classes are represented as boxes with the class name on top. The inheritance relationship is represented by an arrow from the derived class pointing to the base class. The word extends is usually added to the arrow. Note: In an inheritance relationship:
Let’s say you have a base class 0 and you derive from it to create a 1 class. The inheritance relationship states that a 1 is an 0. This means that 1 inherits the interface and implementation of 0, and 1 objects can be used to replace 0 objects in the application.This is known as the Liskov substitution principle. The principle states that “in a computer program, if 8 is a subtype of 9, then objects of type 9 may be replaced with objects of type 8 without altering any of the desired properties of the program”.You’ll see in this article why you should always follow the Liskov substitution principle when creating your class hierarchies, and the problems you’ll run into if you don’t. What’s Composition?Composition is a concept that models a has a relationship. It enables creating complex types by combining objects of other types. This means that a class 02 can contain an object of another class 03. This relationship means that a 02 has a 03.UML represents composition as follows: Composition is represented through a line with a diamond at the composite class pointing to the component class. The composite side can express the cardinality of the relationship. The cardinality indicates the number or valid range of 03 instances the 02 class will contain.In the diagram above, the 08 represents that the 02 class contains one object of type 03. Cardinality can be expressed in the following ways:
Note: Classes that contain objects of other classes are usually referred to as composites, where classes that are used to create more complex types are referred to as components. For example, your 1 class can be composed by another object of type 18. Composition allows you to express that relationship by saying a 1 has a 18.Composition enables you to reuse code by adding objects to other objects, as opposed to inheriting the interface and implementation of other classes. Both 1 and 22 classes can leverage the functionality of 18 through composition without deriving one class from the other.An Overview of Inheritance in PythonEverything in Python is an object. Modules are objects, class definitions and functions are objects, and of course, objects created from classes are objects too. Inheritance is a required feature of every object oriented programming language. This means that Python supports inheritance, and as you’ll see later, it’s one of the few languages that supports multiple inheritance. When you write Python code using classes, you are using inheritance even if you don’t know you’re using it. Let’s take a look at what that means. Remove adsThe Object Super ClassThe easiest way to see inheritance in Python is to jump into the and write a little bit of code. You’ll start by writing the simplest class possible: >>>
You declared a class 24 that doesn’t do much, but it will illustrate the most basic inheritance concepts. Now that you have the class declared, you can use the 25 function to list its members:>>>
returns a list of all the members in the specified object. You have not declared any members in 24, so where is the list coming from? You can find out using the interactive interpreter:>>>
As you can see, the two lists are nearly identical. There are some additional members in 24 like 29 and 30, but every single member of the 31 class is also present in 24.This is because every class you create in Python implicitly derives from 31. You could be more explicit and write 34, but it’s redundant and unnecessary.Note: In Python 2, you have to explicitly derive from 31 for reasons beyond the scope of this article, but you can read about it in the section of the Python 2 documentation.Exceptions Are an ExceptionEvery class that you create in Python will implicitly derive from 31. The exception to this rule are classes used to indicate errors by raising an exception.You can see the problem using the Python interactive interpreter: >>>
You created a new class to indicate a type of error. Then you tried to use it to raise an exception. An exception is raised but the output states that the exception is of type 37 not 38 and that all 39. 40 is a base class provided for all error types. To create a new error type, you must derive your class from 40 or one of its derived classes. The convention in Python is to derive your custom error types from 42, which in turn derives from 40.The correct way to define your error type is the following: >>>
As you can see, when you raise 38, the output correctly states the type of error raised.Creating Class HierarchiesInheritance is the mechanism you’ll use to create hierarchies of related classes. These related classes will share a common interface that will be defined in the base classes. Derived classes can specialize the interface by providing a particular implementation where applies. In this section, you’ll start modeling an HR system. The example will demonstrate the use of inheritance and how derived classes can provide a concrete implementation of the base class interface. The HR system needs to process payroll for the company’s employees, but there are different types of employees depending on how their payroll is calculated. You start by implementing a 45 class that processes payroll:
The 45 implements a 47 method that takes a collection of employees and prints their 48, 49, and check amount using the 47 method exposed on each employee object.Now, you implement a base class 51 that handles the common interface for every employee type:
51 is the base class for all employee types. It is constructed with an 48 and a 49. What you are saying is that every 51 must have an 48 assigned as well as a name.The HR system requires that every 51 processed must provide a 47 interface that returns the weekly salary for the employee. The implementation of that interface differs depending on the type of 51.For example, administrative workers have a fixed salary, so every week they get paid the same amount:
You create a derived class 60 that inherits 51. The class is initialized with the 48 and 49 required by the base class, and you use 64 to initialize the members of the base class. You can read all about 64 in Supercharge Your Classes With Python super(). 60 also requires a 67 initialization parameter that represents the amount the employee makes per week.The class provides the required 47 method used by the HR system. The implementation just returns the amount stored in 67.The company also employs manufacturing workers that are paid by the hour, so you add an 70 to the HR system:
The 70 class is initialized with 48 and 49, like the base class, plus the 74 and the 75 required to calculate the payroll. The 47 method is implemented by returning the hours worked times the hour rate.Finally, the company employs sales associates that are paid through a fixed salary plus a commission based on their sales, so you create a 77 class:
You derive 77 from 60 because both classes have a 67 to consider. At the same time, 77 is initialized with a 82 value that is based on the sales for the employee. 47 leverages the implementation of the base class to retrieve the 84 salary and adds the commission value.Since 77 derives from 60, you have access to the 67 property directly, and you could’ve implemented 47 using the value of that property.The problem with accessing the property directly is that if the implementation of 89 changes, then you’ll have to also change the implementation of 90. It’s better to rely on the already implemented method in the base class and extend the functionality as needed.You created your first class hierarchy for the system. The UML diagram of the classes looks like this: The diagram shows the inheritance hierarchy of the classes. The derived classes implement the 91 interface, which is required by the 45. The 93 implementation requires that the 94 objects passed contain an 48, 49, and 97 implementation.Interfaces are represented similarly to classes with the word interface above the interface name. Interface names are usually prefixed with a capital 98.The application creates its employees and passes them to the payroll system to process payroll: 0You can run the program in the command line and see the results: 1The program creates three employee objects, one for each of the derived classes. Then, it creates the payroll system and passes a list of the employees to its 47 method, which calculates the payroll for each employee and prints the results.Notice how the 51 base class doesn’t define a 47 method. This means that if you were to create a plain 51 object and pass it to the 45, then you’d get an error. You can try it in the Python interactive interpreter:>>> 2While you can instantiate an 51 object, the object can’t be used by the 45. Why? Because it can’t 47 for an 51. To meet the requirements of 45, you’ll want to convert the 51 class, which is currently a concrete class, to an abstract class. That way, no employee is ever just an 51, but one that implements 47.Remove adsAbstract Base Classes in PythonThe 51 class in the example above is what is called an abstract base class. Abstract base classes exist to be inherited, but never instantiated. Python provides the 13 module to define abstract base classes.You can use leading underscores in your class name to communicate that objects of that class should not be created. Underscores provide a friendly way to prevent misuse of your code, but they don’t prevent eager users from creating instances of that class. The in the Python standard library provides functionality to prevent creating objects from abstract base classes. You can modify the implementation of the 51 class to ensure that it can’t be instantiated: 3You derive 51 from 17, making it an abstract base class. Then, you decorate the 47 method with the 19 decorator.This change has two nice side-effects:
You can see that objects of type 51 can’t be created using the interactive interpreter:>>> 4The output shows that the class cannot be instantiated because it contains an abstract method 97. Derived classes must override the method to allow creating objects of their type.Implementation Inheritance vs Interface InheritanceWhen you derive one class from another, the derived class inherits both:
Most of the time, you’ll want to inherit the implementation of a class, but you will want to implement multiple interfaces, so your objects can be used in different situations. Modern programming languages are designed with this basic concept in mind. They allow you to inherit from a single class, but you can implement multiple interfaces. In Python, you don’t have to explicitly declare an interface. Any object that implements the desired interface can be used in place of another object. This is known as . Duck typing is usually explained as “if it behaves like a duck, then it’s a duck.” To illustrate this, you will now add a 26 class to the example above which doesn’t derive from 51: 5The 26 class doesn’t derive from 51, but it exposes the same interface required by the 45. The 93 requires a list of objects that implement the following interface:
All these requirements are met by the 26 class, so the 45 can still calculate its payroll.You can modify the program to use the 26 class: 6The program creates a 26 object and adds it to the list processed by the 45. You can now run the program and see its output: 7As you can see, the 45 can still process the new object because it meets the desired interface.Since you don’t have to derive from a specific class for your objects to be reusable by the program, you may be asking why you should use inheritance instead of just implementing the desired interface. The following rules may help you:
You can now clean up the example above to move onto the next topic. You can delete the 46 file and then modify the 21 module to its original state: 8You removed the import of the 13 module since the 51 class doesn’t need to be abstract. You also removed the abstract 97 method from it since it doesn’t provide any implementation.Basically, you are inheriting the implementation of the 48 and 49 attributes of the 51 class in your derived classes. Since 47 is just an interface to the 93 method, you don’t need to implement it in the 51 base class.Notice how the 77 class derives from 60. This means that 77 inherits the implementation and interface of 60. You can see how the 90 method leverages the base class implementation because it relies on the result from 62 to implement its own version.Remove adsThe Class Explosion ProblemIf you are not careful, inheritance can lead you to a huge hierarchical structure of classes that is hard to understand and maintain. This is known as the class explosion problem. You started building a class hierarchy of 51 types used by the 45 to calculate payroll. Now, you need to add some functionality to those classes, so they can be used with the new 65.The 65 tracks productivity based on employee roles. There are different employee roles:
With those requirements, you start to see that 51 and its derived classes might belong somewhere other than the 21 module because now they’re also used by the 65.You create an 70 module and move the classes there: 9The implementation remains the same, but you move the classes to the 94 module. Now, you change your program to support the change: 0You run the program and verify that it still works: 1With everything in place, you start adding the new classes: 2First, you add a 72 class that derives from 60. The class exposes a method 74 that will be used by the productivity system. The method takes the 75 the employee worked.Then you add 76, 77, and 78 and then implement the 74 interface, so they can be used by the productivity system.Now, you can add the 80 class: 3The class tracks employees in the 81 method that takes a list of employees and the number of hours to track. You can now add the productivity system to your program: 4The program creates a list of employees of different types. The employee list is sent to the productivity system to track their work for 40 hours. Then the same list of employees is sent to the payroll system to calculate their payroll. You can run the program to see the output: 5The program shows the employees working for 40 hours through the productivity system. Then it calculates and displays the payroll for each of the employees. The program works as expected, but you had to add four new classes to support the changes. As new requirements come, your class hierarchy will inevitably grow, leading to the class explosion problem where your hierarchies will become so big that they’ll be hard to understand and maintain. The following diagram shows the new class hierarchy: The diagram shows how the class hierarchy is growing. Additional requirements might have an exponential effect in the number of classes with this design. Remove adsInheriting Multiple ClassesPython is one of the few modern programming languages that supports multiple inheritance. Multiple inheritance is the ability to derive a class from multiple base classes at the same time. Multiple inheritance has a bad reputation to the extent that most modern programming languages don’t support it. Instead, modern programming languages support the concept of interfaces. In those languages, you inherit from a single base class and then implement multiple interfaces, so your class can be re-used in different situations. This approach puts some constraints in your designs. You can only inherit the implementation of one class by directly deriving from it. You can implement multiple interfaces, but you can’t inherit the implementation of multiple classes. This constraint is good for software design because it forces you to design your classes with fewer dependencies on each other. You will see later in this article that you can leverage multiple implementations through composition, which makes software more flexible. This section, however, is about multiple inheritance, so let’s take a look at how it works. It turns out that sometimes temporary secretaries are hired when there is too much paperwork to do. The 82 class performs the role of a 76 in the context of the 65, but for payroll purposes, it is an 70.You look at your class design. It has grown a little bit, but you can still understand how it works. It seems you have two options:
Then, you remember that Python supports multiple inheritance, so you decide to derive from both 76 and 70: 6Python allows you to inherit from two different classes by specifying them between parenthesis in the class declaration. Now, you modify your program to add the new temporary secretary employee: 7You run the program to test it: 8You get a exception saying that 99 positional arguments where expected, but 00 were given.This is because you derived 82 first from 76 and then from 70, so the interpreter is trying to use 04 to initialize the object.Okay, let’s reverse it: 9Now, run the program again and see what happens: 0Now it seems you are missing a 67 parameter, which is necessary to initialize 76, but that parameter doesn’t make sense in the context of a 82 because it’s an 70.Maybe implementing 09 will help: 1Try it: 2That didn’t work either. Okay, it’s time for you to dive into Python’s method resolution order (MRO) to see what’s going on. When a method or attribute of a class is accessed, Python uses the class MRO to find it. The MRO is also used by 64 to determine which method or attribute to invoke. You can learn more about 64 in Supercharge Your Classes With Python super().You can evaluate the 82 class MRO using the interactive interpreter:>>> 3The MRO shows the order in which Python is going to look for a matching attribute or method. In the example, this is what happens when we create the 82 object:
Because the parameters don’t match, a 37 exception is raised.You can bypass the MRO by reversing the inheritance order and directly calling 22 as follows: 4That solves the problem of creating the object, but you will run into a similar problem when trying to calculate payroll. You can run the program to see the problem: 5The problem now is that because you reversed the inheritance order, the MRO is finding the 47 method of 24 before the one in 70. You need to override 47 in 82 and invoke the right implementation from it: 6The 97 method directly invokes 29 to ensure that you get the correct result. You can run the program again to see it working: 7The program now works as expected because you’re forcing the method resolution order by explicitly telling the interpreter which method we want to use. As you can see, multiple inheritance can be confusing, especially when you run into the . The following diagram shows the diamond problem in your class hierarchy: The diagram shows the diamond problem with the current class design. 82 uses multiple inheritance to derive from two classes that ultimately also derive from 51. This causes two paths to reach the 51 base class, which is something you want to avoid in your designs.The diamond problem appears when you’re using multiple inheritance and deriving from two classes that have a common base class. This can cause the wrong version of a method to be called. As you’ve seen, Python provides a way to force the right method to be invoked, and analyzing the MRO can help you understand the problem. Still, when you run into the diamond problem, it’s better to re-think the design. You will now make some changes to leverage multiple inheritance, avoiding the diamond problem. The 51 derived classes are used by two different systems:
This means that everything related to productivity should be together in one module and everything related to payroll should be together in another. You can start making changes to the productivity module: 8The 34 module implements the 65 class, as well as the related roles it supports. The classes implement the 74 interface required by the system, but they don’t derived from 51.You can do the same with the 21 module: 9The 21 module implements the 45, which calculates payroll for the employees. It also implements the policy classes for payroll. As you can see, the policy classes don’t derive from 51 anymore.You can now add the necessary classes to the 94 module: 0The 70 module imports policies and roles from the other modules and implements the different 51 types. You are still using multiple inheritance to inherit the implementation of the salary policy classes and the productivity roles, but the implementation of each class only needs to deal with initialization.Notice that you still need to explicitly initialize the salary policies in the constructors. You probably saw that the initializations of 72 and 76 are identical. Also, the initializations of 78 and 82 are the same.You will not want to have this kind of code duplication in more complex designs, so you have to be careful when designing class hierarchies. Here’s the UML diagram for the new design: The diagram shows the relationships to define the 76 and 82 using multiple inheritance, but avoiding the diamond problem.You can run the program and see how it works: 1You’ve seen how inheritance and multiple inheritance work in Python. You can now explore the topic of composition. Remove adsComposition in PythonComposition is an object oriented design concept that models a has a relationship. In composition, a class known as composite contains an object of another class known to as component. In other words, a composite class has a component of another class. Composition allows composite classes to reuse the implementation of the components it contains. The composite class doesn’t inherit the component class interface, but it can leverage its implementation. The composition relation between two classes is considered loosely coupled. That means that changes to the component class rarely affect the composite class, and changes to the composite class never affect the component class. This provides better adaptability to change and allows applications to introduce new requirements without affecting existing code. When looking at two competing software designs, one based on inheritance and another based on composition, the composition solution usually is the most flexible. You can now look at how composition works. You’ve already used composition in our examples. If you look at the 51 class, you’ll see that it contains two attributes:
These two attributes are objects that the 51 class has. Therefore, you can say that an 51 has an 48 and has a name.Another attribute for an 51 might be an 58: 2You implemented a basic address class that contains the usual components for an address. You made the 59 attribute optional because not all addresses will have that component.You implemented 60 to provide a pretty representation of an 58. You can see this implementation in the interactive interpreter:>>> 3When you 62 the 63 variable, the special method 60 is invoked. Since you overloaded the method to return a string formatted as an address, you get a nice, readable representation. Operator and Function Overloading in Custom Python Classes gives a good overview of the special methods available in classes that can be implemented to customize the behavior of your objects.You can now add the 58 to the 51 class through composition: 4You initialize the 63 attribute to 68 for now to make it optional, but by doing that, you can now assign an 58 to an 51. Also notice that there is no reference in the 94 module to the 72 module.Composition is a loosely coupled relationship that often doesn’t require the composite class to have knowledge of the component. The UML diagram representing the relationship between 51 and 58 looks like this:The diagram shows the basic composition relationship between 51 and 58.You can now modify the 45 class to leverage the 63 attribute in 51: 5You check to see if the 94 object has an address, and if it does, you print it. You can now modify the program to assign some addresses to the employees: 6You added a couple of addresses to the 81 and 82 objects. When you run the program, you will see the addresses printed: 7Notice how the payroll output for the 81 and 82 objects show the addresses where the checks were sent.The 51 class leverages the implementation of the 58 class without any knowledge of what an 58 object is or how it’s represented. This type of design is so flexible that you can change the 58 class without any impact to the 51 class.Remove adsFlexible Designs With CompositionComposition is more flexible than inheritance because it models a loosely coupled relationship. Changes to a component class have minimal or no effects on the composite class. Designs based on composition are more suitable to change. You change behavior by providing new components that implement those behaviors instead of adding new classes to your hierarchy. Take a look at the multiple inheritance example above. Imagine how new payroll policies will affect the design. Try to picture what the class hierarchy will look like if new roles are needed. As you saw before, relying too heavily on inheritance can lead to class explosion. The biggest problem is not so much the number of classes in your design, but how tightly coupled the relationships between those classes are. Tightly coupled classes affect each other when changes are introduced. In this section, you are going to use composition to implement a better design that still fits the requirements of the 45 and the 65.You can start by implementing the functionality of the 65: 8The 65 class defines some roles using a string identifier mapped to a role class that implements the role. It exposes a 94 method that, given a role identifier, returns the role type object. If the role is not found, then a 95 exception is raised.It also exposes the previous functionality in the 96 method, where given a list of employees it tracks the productivity of those employees.You can now implement the different role classes: 9Each of the roles you implemented expose a 97 that takes the number of 75 worked. The methods return a string representing the duties.The role classes are independent of each other, but they expose the same interface, so they are interchangeable. You’ll see later how they are used in the application. Now, you can implement the 45 for the application: 0The 45 keeps an internal database of payroll policies for each employee. It exposes a 01 that, given an employee 48, returns its payroll policy. If a specified 48 doesn’t exist in the system, then the method raises a 95 exception.The implementation of 47 works the same as before. It takes a list of employees, calculates the payroll, and prints the results.You can now implement the payroll policy classes: 1You first implement a 06 class that serves as a base class for all the payroll policies. This class tracks the 74, which is common to all payroll policies.The other policy classes derive from 06. We use inheritance here because we want to leverage the implementation of 06. Also, 10, 11, and 12 are a 06. 10 is initialized with a 67 value that is then used in 47. 11 is initialized with the 75, and implements 47 by leveraging the base class 74.The 12 class derives from 10 because it wants to inherit its implementation. It is initialized with the 67 parameters, but it also requires a 24 parameter.The 24 is used to calculate the 26, which is implemented as a property so it gets calculated when requested. In the example, we are assuming that a sale happens every 5 hours worked, and the 26 is the number of sales times the 24 value. 12 implements the 47 method by first leveraging the implementation in 10 and then adding the calculated commission.You can now add an 32 class to manage employee addresses: 2The 32 class keeps an internal database of 58 objects for each employee. It exposes a 35 method that returns the address of the specified employee 48. If the employee 48 doesn’t exist, then it raises a 95.The 58 class implementation remains the same as before: 2The class manages the address components and provides a pretty representation of an address. So far, the new classes have been extended to support more functionality, but there are no significant changes to the previous design. This is going to change with the design of the 70 module and its classes.You can start by implementing an 41 class: 4The 41 keeps track of all the employees in the company. For each employee, it tracks the 48, 49, and 45. It has an instance of the 65, the 45, and the 32. These instances are used to create employees.It exposes an 49 property that returns the list of employees. The 51 objects are created in an internal method 51. Notice that you don’t have different types of 51 classes. You just need to implement a single 51 class: 5The 51 class is initialized with the 48, 49, and 63 attributes. It also requires the productivity 45 for the employee and the 59 policy.The class exposes a 88 method that takes the hours worked. This method first retrieves the 61 from the 45. In other words, it delegates to the 45 object to perform its duties.In the same way, it delegates to the 59 object to track the work 75. The 59, as you saw, uses those hours to calculate the payroll if needed.The following diagram shows the composition design used: The diagram shows the design of composition based policies. There is a single 51 that is composed of other data objects like 58 and depends on the 69 and 91 interfaces to delegate the work. There are multiple implementations of these interfaces.You can now use this design in your program: 6You can run the program to see its output: 7This design is what is called , where classes are composed of policies, and they delegate to those policies to do the work. Policy-based design was introduced in the book Modern C++ Design, and it uses template metaprogramming in C++ to achieve the results. Python does not support templates, but you can achieve similar results using composition, as you saw in the example above. This type of design gives you all the flexibility you’ll need as requirements change. Imagine you need to change the way payroll is calculated for an object at run-time. Remove adsCustomizing Behavior With CompositionIf your design relies on inheritance, you need to find a way to change the type of an object to change its behavior. With composition, you just need to change the policy the object uses. Imagine that our 81 all of a sudden becomes a temporary employee that gets paid by the hour. You can modify the object during the execution of the program in the following way: 8The program gets the employee list from the 41 and retrieves the first employee, which is the manager we want. Then it creates a new 11 initialized at $55 per hour and assigns it to the manager object.The new policy is now used by the 45 modifying the existing behavior. You can run the program again to see the result: 9The check for Mary Poppins, our manager, is now for $2200 instead of the fixed salary of $3000 that she had per week. Notice how we added that business rule to the program without changing any of the existing classes. Consider what type of changes would’ve been required with an inheritance design. You would’ve had to create a new class and change the type of the manager employee. There is no chance you could’ve changed the policy at run-time. Choosing Between Inheritance and Composition in PythonSo far, you’ve seen how inheritance and composition work in Python. You’ve seen that derived classes inherit the interface and implementation of their base classes. You’ve also seen that composition allows you to reuse the implementation of another class. You’ve implemented two solutions to the same problem. The first solution used multiple inheritance, and the second one used composition. You’ve also seen that Python’s duck typing allows you to reuse objects with existing parts of a program by implementing the desired interface. In Python, it isn’t necessary to derive from a base class for your classes to be reused. At this point, you might be asking when to use inheritance vs composition in Python. They both enable code reuse. Inheritance and composition can tackle similar problems in your Python programs. The general advice is to use the relationship that creates fewer dependencies between two classes. This relation is composition. Still, there will be times where inheritance will make more sense. The following sections provide some guidelines to help you make the right choice between inheritance and composition in Python. Inheritance to Model “Is A” RelationshipInheritance should only be used to model an is a relationship. Liskov’s substitution principle says that an object of type 6, which inherits from 7, can replace an object of type 7 without altering the desirable properties of a program.Liskov’s substitution principle is the most important guideline to determine if inheritance is the appropriate design solution. Still, the answer might not be straightforward in all situations. Fortunately, there is a simple test you can use to determine if your design follows Liskov’s substitution principle. Let’s say you have a class 78 that provides an implementation and interface you want to reuse in another class 79. Your initial thought is that you can derive 79 from 78 and inherit both the interface and implementation. To be sure this is the right design, you follow theses steps:
If you can justify both relationships, then you should never inherit those classes from one another. Let’s look at a more concrete example. You have a class 86 which exposes an 87 property. You need a class 88, which also has an 87. It seems that a 88 is a special type of 86, so maybe you can derive from it and leverage both the interface and implementation.Before you jump into the implementation, you use Liskov’s substitution principle to evaluate the relationship. A 88 is a 86 because its area is calculated from the product of its 94 times its 95. The constraint is that 96 and 97 must be equal.It makes sense. You can justify the relationship and explain why a 88 is a 86. Let’s reverse the relationship to see if it makes sense.A 86 is a 88 because its area is calculated from the product of its 94 times its 95. The difference is that 04 and 05 can change independently.It also makes sense. You can justify the relationship and describe the special constraints for each class. This is a good sign that these two classes should never derive from each other. You might have seen other examples that derive 88 from 86 to explain inheritance. You might be skeptical with the little test you just did. Fair enough. Let’s write a program that illustrates the problem with deriving 88 from 86.First, you implement 86. You’re even going to encapsulate the attributes to ensure that all the constraints are met: 0The 86 class is initialized with a 95 and a 94, and it provides an 87 property that returns the area. The 95 and 94 are encapsulated to avoid changing them directly.Now, you derive 88 from 86 and override the necessary interface to meet the constraints of a 88: 1The 88 class is initialized with a 21, which is used to initialize both components of the base class. Now, you write a small program to test the behavior: 2The program creates a 86 and a 88 and asserts that their 87 is calculated correctly. You can run the program and see that everything is 25 so far: 3The program executes correctly, so it seems that 88 is just a special case of a 86.Later on, you need to support resizing 86 objects, so you make the appropriate changes to the class: 4 29 takes the 30 and 31 for the object. You can add the following code to the program to verify that it works correctly: 5You resize the rectangle object and assert that the new area is correct. You can run the program to verify the behavior: 3The assertion passes, and you see that the program runs correctly. So, what happens if you resize a square? Modify the program, and try to modify the 32 object: 7You pass the same parameters to 33 that you used with 34, and print the area. When you run the program you see: 8The program shows that the new area is 35 like the 34 object. The problem now is that the 32 object no longer meets the 88 class constraint that the 95 and 94 must be equal.How can you fix that problem? You can try several approaches, but all of them will be awkward. You can override 29 in 32 and ignore the 94 parameter, but that will be confusing for people looking at other parts of the program where 44 are being resized and some of them are not getting the expected areas because they are really 45.In a small program like this one, it might be easy to spot the causes of the weird behavior, but in a more complex program, the problem will be harder to find. The reality is that if you’re able to justify an inheritance relationship between two classes both ways, you should not derive one class from another. In the example, it doesn’t make sense that 88 inherits the interface and implementation of 29 from 86. That doesn’t mean that 88 objects can’t be resized. It means that the interface is different because it only needs a 21 parameter.This difference in interface justifies not deriving 88 from 86 like the test above advised.Remove adsMixing Features With Mixin ClassesOne of the uses of multiple inheritance in Python is to extend a class features through mixins. A mixin is a class that provides methods to other classes but are not considered a base class. A mixin allows other classes to reuse its interface and implementation without becoming a super class. They implement a unique behavior that can be aggregated to other unrelated classes. They are similar to composition but they create a stronger relationship. Let’s say you want to convert objects of certain types in your application to a dictionary representation of the object. You could provide a 53 method in every class that you want to support this feature, but the implementation of 53 seems to be very similar.This could be a good candidate for a mixin. You start by slightly modifying the 51 class from the composition example: 9The change is very small. You just changed the 45 and 59 attributes to be internal by adding a leading underscore to their name. You will see soon why you are making that change.Now, you add the 58 class: 0The 58 class exposes a 53 method that returns the representation of itself as a dictionary. The method is implemented as a 61 comprehension that says, “Create a dictionary mapping 62 to 63 for each item in 64 if the 62 is not internal.”Note: This is why we made the role and payroll attributes internal in the 51 class, because we don’t want to represent them in the dictionary.As you saw at the beginning, creating a class inherits some members from 31, and one of those members is 29, which is basically a mapping of all the attributes in an object to their value.You iterate through all the items in 29 and filter out the ones that have a name that starts with an underscore using 70. 71 checks the specified value. If the value is an 31, then it looks to see if it also has a 53 member and uses it to represent the object. Otherwise, it returns a string representation. If the value is not an 31, then it simply returns the value.You can modify the 51 class to support this mixin: 1All you have to do is inherit the 58 to support the functionality. It will be nice to support the same functionality in the 58 class, so the 78 attribute is represented in the same way: 2You apply the mixin to the 58 class to support the feature. Now, you can write a small program to test it: 3The program implements a 80 that converts the dictionary to a JSON string using indentation so the output looks better.Then, it iterates through all the employees, printing the dictionary representation provided by 53. You can run the program to see its output: 4You leveraged the implementation of 58 in both 51 and 58 classes even when they are not related. Because 58 only provides behavior, it is easy to reuse with other classes without causing problems.Remove adsComposition to Model “Has A” RelationshipComposition models a has a relationship. With composition, a class 02 has an instance of class 03 and can leverage its implementation. The 03 class can be reused in other classes completely unrelated to the 02.In the composition example above, the 51 class has an 58 object. 58 implements all the functionality to handle addresses, and it can be reused by other classes.Other classes like 41 or 94 can reuse 58 without being related to 51. They can leverage the same implementation ensuring that addresses are handled consistently across the application.A problem you may run into when using composition is that some of your classes may start growing by using multiple components. Your classes may require multiple parameters in the constructor just to pass in the components they are made of. This can make your classes hard to use. A way to avoid the problem is by using the Factory Method to construct your objects. You did that with the composition example. If you look at the implementation of the 41 class, you’ll notice that it uses 51 to construct an 51 object with the right parameters.This design will work, but ideally, you should be able to construct an 51 object just by specifying an 48, for example 02.The following changes might improve your design. You can start with the 34 module: 5First, you make the 04 class internal, and then provide a 05 internal variable to the module. You are communicating to other developers that they should not create or use the 04 directly. Instead, you provide two functions, 07 and 81, as the public interface to the module. This is what other modules should use.What you are saying is that the 04 is a Singleton, and there should only be one object created from it.Now, you can do the same with the 21 module: 6Again, you make the 11 internal and provide a public interface to it. The application will use the public interface to get policies and calculate payroll.You will now do the same with the 72 module: 7You are basically saying that there should only be one 13, one 11, and one 04. Again, this design pattern is called the Singleton design pattern, which comes in handy for classes from which there should only be one, single instance.Now, you can work on the 70 module. You will also make a Singleton out of the 17, but you will make some additional changes: 8You first import the relevant functions and classes from other modules. The 17 is made internal, and at the bottom, you create a single instance. This instance is public and part of the interface because you will want to use it in the application.You changed the 19 attribute to be a dictionary where the key is the employee 48 and the value is the employee information. You also exposed a 21 method to return the information for the specified employee 22.The 23 property now sorts the keys to return the employees sorted by their 48. You replaced the method that constructed the 51 objects with calls to the 51 initializer directly.The 51 class now is initialized with the 48 and uses the public functions exposed in the other modules to initialize its attributes.You can now change the program to test the changes: 9You import the relevant functions from the 21 and 34 modules, as well as the 31 and 51 class. The program is cleaner because you exposed the required interface and encapsulated how objects are accessed.Notice that you can now create an 51 object directly just using its 48. You can run the program to see its output: 0The program works the same as before, but now you can see that a single 51 object can be created from its 48 and display its dictionary representation.Take a closer look at the 51 class: 1The 51 class is a composite that contains multiple objects providing different functionality. It contains an 58 that implements all the functionality related to where the employee lives. 51 also contains a productivity role provided by the 34 module, and a payroll policy provided by the 21 module. These two objects provide implementations that are leveraged by the 51 class to track work in the 88 method and to calculate the payroll in the 47 method.You are using composition in two different ways. The 58 class provides additional data to 51 where the role and payroll objects provide additional behavior.Still, the relationship between 51 and those objects is loosely coupled, which provides some interesting capabilities that you’ll see in the next section.Composition to Change Run-Time BehaviorInheritance, as opposed to composition, is a tightly couple relationship. With inheritance, there is only one way to change and customize behavior. Method overriding is the only way to customize the behavior of a base class. This creates rigid designs that are difficult to change. Composition, on the other hand, provides a loosely coupled relationship that enables flexible designs and can be used to change behavior at run-time. Imagine you need to support a long-term disability (LTD) policy when calculating payroll. The policy states that an employee on LTD should be paid 60% of their weekly salary assuming 40 hours of work. With an inheritance design, this can be a very difficult requirement to support. Adding it to the composition example is a lot easier. Let’s start by adding the policy class: 2Notice that 49 doesn’t inherit 06, but implements the same interface. This is because the implementation is completely different, so we don’t want to inherit any of the 06 implementation.The 49 initializes 53 to 68, and provides an internal 55 method that raises an exception if the 56 has not been applied. Then, it provides a 57 method to assign the 53.The public interface first checks that the 53 has been applied, and then implements the functionality in terms of that base policy. The 60 method just delegates to the base policy, and 47 uses it to calculate the 62 and then return the 60%.You can now make a small change to the 51 class: 3You added an 64 method that applies the existing payroll policy to the new policy and then substitutes it. You can now modify the program to apply the policy to an 51 object: 4The program accesses 66, which is located at index 67, creates the 49 object, and applies the policy to the employee. When 47 is called, the change is reflected. You can run the program to evaluate the output: 5The check amount for employee Kevin Bacon, who is the sales employee, is now for $1080 instead of $1800. That’s because the 49 has been applied to the salary.As you can see, you were able to support the changes just by adding a new policy and modifying a couple interfaces. This is the kind of flexibility that policy design based on composition gives you. Choosing Between Inheritance and Composition in PythonPython, as an object oriented programming language, supports both inheritance and composition. You saw that inheritance is best used to model an is a relationship, whereas composition models a has a relationship. Sometimes, it’s hard to see what the relationship between two classes should be, but you can follow these guidelines:
ConclusionYou explored inheritance and composition in Python. You learned about the type of relationships that inheritance and composition create. You also went through a series of exercises to understand how inheritance and composition are implemented in Python. In this article, you learned how to:
Recommended ReadingHere are some books and articles that further explore object oriented design and can be useful to help you understand the correct use of inheritance and composition in Python or other languages:
Mark as Completed Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Inheritance and Composition: A Python OOP Guide 🐍 Python Tricks 💌 Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team. Send Me Python Tricks » About Isaac Rodriguez Hi, I'm Isaac. I build, lead, and mentor software development teams, and for the past few years I've been focusing on cloud services and back-end applications using Python among other languages. Love to hear from you here at Real Python. » More about IsaacEach tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are: Alex Aldren Joanna Master Real-World Python Skills With Unlimited Access to Real Python Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas: Level Up Your Python Skills » Master Real-World Python Skills Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas: Level Up Your Python Skills » What Do You Think? Rate this article: Tweet Share Share EmailWhat’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know. Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. and get answers to common questions in our support portal. When a class is derived from several base classes it is called as?A derived class with several base classes is called as multiple inheritance.
When a class serves as base class for many derived classes?When a class serves as base class for many derived classes, the situation is called: polymorphism.
When a class is derived from a base class it gets access to?A derived class can access all the non-private members of its base class. Thus base-class members that should not be accessible to the member functions of derived classes should be declared private in the base class. Constructors, destructors and copy constructors of the base class.
What are base classes also called?The base class is also called superclass or parent class. The derived class is also called a subclass or child class.
|