
Object Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class (called the child or subclass) to inherit properties and methods from another class (called the parent or superclass). This relationship between classes promotes code reusability and modularity, making it easier to maintain and expand your codebase.
- Basics of Python Classes and Objects
- Understanding Inheritance in Python
- Single Inheritance
- Multiple Inheritance
- Multilevel Inheritance
- Hierarchical Inheritance
- Hybrid Inheritance
- Creating Parent and Child Classes
- Overriding Methods in Child Classes
- Using the Super() Function
- Method Resolution Order (MRO)
- Composition vs. Inheritance
Python, being a versatile and powerful programming language, fully supports object inheritance, allowing developers to create complex hierarchies of classes that can inherit attributes and behaviors from one another. Inheritance enables you to build on existing code without the need to rewrite or duplicate functionality, ultimately leading to a more efficient development process.
In this tutorial, we will explore the basics of object inheritance in Python, starting with a brief introduction to classes and objects. We will then delve into various types of inheritance, how to create parent and child classes, and how to override methods. Additionally, we will discuss the method resolution order, composition as an alternative to inheritance, and real-world examples of object inheritance.
Basics of Python Classes and Objects
Before diving into object inheritance, it’s essential to understand the basics of Python classes and objects. Classes are blueprints for creating objects, while objects are instances of a class, each with their own set of attributes and methods.
- Classes: Classes define the structure and behavior of objects. They consist of data members (attributes) and member functions (methods) that describe the properties and actions of objects. To define a class in Python, we use the
class
keyword, followed by the class name and a colon.
class MyClass:
# Attributes and methods go here
- Attributes: Attributes are variables that belong to an object. They store the state of the object. In Python, you can define attributes within a class using the
__init__
method, which is called when an object is instantiated.
class MyClass:
def __init__(self, attribute1, attribute2):
self.attribute1 = attribute1
self.attribute2 = attribute2
- Methods: Methods are functions that belong to an object and define its behavior. They can modify the object’s state and interact with other objects. In Python, you can define methods within a class using the
def
keyword, followed by the method name and a pair of parentheses containing the method’s parameters.
class MyClass:
# ... Attributes ...
def my_method(self, parameter1, parameter2):
# Method implementation
- Objects: Objects are instances of a class, created by calling the class like a function and passing the required parameters. Each object has its own set of attributes and can call the methods defined in its class.
my_object = MyClass("value1", "value2")
my_object.my_method("param1", "param2")
Now that we have a basic understanding of Python classes and objects, we can explore the concept of object inheritance and how it helps to create more organized and reusable code.
Understanding Inheritance in Python
Inheritance is a core feature of object-oriented programming that allows a class to inherit attributes and methods from another class. It creates a parent-child relationship between classes, promoting code reusability and modularity. In Python, inheritance is easily achieved using the following concepts:
- Parent Class (Superclass): A parent class, also known as a superclass or base class, is the class from which other classes inherit attributes and methods. It contains common functionalities and characteristics that will be shared by all its child classes.
class ParentClass:
# Attributes and methods go here
- Child Class (Subclass): A child class, also known as a subclass or derived class, is the class that inherits attributes and methods from a parent class. It can also define its own attributes and methods or override the inherited ones, allowing for customization and extension of the parent class’s behavior.
class ChildClass(ParentClass):
# Additional attributes and methods or overridden methods go here
- Inheriting from a Parent Class: To create a child class that inherits from a parent class, you simply need to include the name of the parent class in parentheses right after the child class name.
class ParentClass:
# Attributes and methods go here
class ChildClass(ParentClass):
# Additional attributes and methods or overridden methods go here
- Accessing Parent Class Attributes and Methods: In a child class, you can access the parent class’s attributes and methods by referring to them with the
self
keyword. This allows you to use or extend the parent class’s functionality within the child class.
class ParentClass:
def parent_method(self):
# Method implementation
class ChildClass(ParentClass):
def child_method(self):
self.parent_method()
By understanding the basics of inheritance in Python, you can create more organized, reusable, and extensible code, taking advantage of the parent-child relationship between classes to share common functionalities and characteristics. In the following sections, we will explore different types of inheritance, how to create and customize parent and child classes, and other related concepts.
Single Inheritance
Single inheritance is the most basic form of inheritance in object-oriented programming, where a child class inherits attributes and methods from a single parent class. This type of inheritance promotes code reusability by allowing a child class to use or extend the functionality of its parent class without the need to rewrite or duplicate code. In Python, single inheritance can be easily implemented using the following approach:
- Define the Parent Class: First, create the parent class with its attributes and methods. This class will serve as the base class for your child class.
class ParentClass:
def __init__(self, attribute1):
self.attribute1 = attribute1
def parent_method(self):
print("This is a method in the parent class.")
- Define the Child Class: Next, create the child class by including the parent class’s name in parentheses right after the child class name. This indicates that the child class will inherit from the parent class.
class ChildClass(ParentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def child_method(self):
print("This is a method in the child class.")
In the example above, the ChildClass
inherits from ParentClass
, meaning it has access to its attributes and methods. The super().__init__(attribute1)
call in the ChildClass
constructor is used to initialize the parent class’s attributes.
- Create Objects and Test Inheritance: Now, you can create instances of both the parent and child classes, and verify that the child class has access to the parent class’s attributes and methods.
parent_object = ParentClass("value1")
parent_object.parent_method()
child_object = ChildClass("value1", "value2")
child_object.parent_method()
child_object.child_method()
In this example, the child_object
has access to both parent_method()
and child_method()
. This demonstrates single inheritance, where the ChildClass
inherits attributes and methods from the ParentClass
.
Multiple Inheritance
Multiple inheritance is a more advanced form of inheritance in object-oriented programming, where a child class inherits attributes and methods from more than one parent class. This allows a child class to combine functionalities and characteristics from multiple parent classes, promoting code reusability and modularity. In Python, multiple inheritance can be implemented using the following approach:
- Define the Parent Classes: First, create two or more parent classes with their attributes and methods. These classes will serve as the base classes for your child class.
class ParentClass1:
def __init__(self, attribute1):
self.attribute1 = attribute1
def method1(self):
print("This is a method in ParentClass1.")
class ParentClass2:
def __init__(self, attribute2):
self.attribute2 = attribute2
def method2(self):
print("This is a method in ParentClass2.")
- Define the Child Class: Next, create the child class by including the names of the parent classes in parentheses, separated by commas, right after the child class name. This indicates that the child class will inherit from multiple parent classes.
class ChildClass(ParentClass1, ParentClass2):
def __init__(self, attribute1, attribute2, attribute3):
ParentClass1.__init__(self, attribute1)
ParentClass2.__init__(self, attribute2)
self.attribute3 = attribute3
def child_method(self):
print("This is a method in the child class.")
In the example above, the ChildClass
inherits from both ParentClass1
and ParentClass2
, meaning it has access to their attributes and methods. The ParentClass1.__init__(self, attribute1)
and ParentClass2.__init__(self, attribute2)
calls in the ChildClass
constructor are used to initialize the parent classes’ attributes.
- Create Objects and Test Inheritance: Now, you can create instances of the parent and child classes, and verify that the child class has access to the attributes and methods of both parent classes.
parent1_object = ParentClass1("value1")
parent1_object.method1()
parent2_object = ParentClass2("value2")
parent2_object.method2()
child_object = ChildClass("value1", "value2", "value3")
child_object.method1()
child_object.method2()
child_object.child_method()
In this example, the child_object
has access to method1()
from ParentClass1
, method2()
from ParentClass2
, and child_method()
from ChildClass
. This demonstrates multiple inheritance, where the ChildClass
inherits attributes and methods from both ParentClass1
and ParentClass2
.
Multilevel Inheritance
Multilevel inheritance is another form of inheritance in object-oriented programming, where a child class inherits from a parent class, and that parent class is itself a child class of another class. This creates a chain or hierarchy of classes, allowing attributes and methods to be passed down through multiple levels. In Python, multilevel inheritance can be implemented using the following approach:
- Define the Grandparent Class: First, create the grandparent class with its attributes and methods. This class will serve as the base class for the parent class.
class GrandparentClass:
def __init__(self, attribute1):
self.attribute1 = attribute1
def grandparent_method(self):
print("This is a method in the grandparent class.")
- Define the Parent Class: Next, create the parent class by including the name of the grandparent class in parentheses right after the parent class name. This indicates that the parent class will inherit from the grandparent class.
class ParentClass(GrandparentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def parent_method(self):
print("This is a method in the parent class.")
- Define the Child Class: Now, create the child class by including the name of the parent class in parentheses right after the child class name. This indicates that the child class will inherit from the parent class, which in turn inherits from the grandparent class.
class ChildClass(ParentClass):
def __init__(self, attribute1, attribute2, attribute3):
super().__init__(attribute1, attribute2)
self.attribute3 = attribute3
def child_method(self):
print("This is a method in the child class.")
- Create Objects and Test Inheritance: Finally, you can create instances of the grandparent, parent, and child classes, and verify that the child class has access to the attributes and methods of both its parent class and grandparent class.
grandparent_object = GrandparentClass("value1")
grandparent_object.grandparent_method()
parent_object = ParentClass("value1", "value2")
parent_object.grandparent_method()
parent_object.parent_method()
child_object = ChildClass("value1", "value2", "value3")
child_object.grandparent_method()
child_object.parent_method()
child_object.child_method()
In this example, the child_object
has access to grandparent_method()
from GrandparentClass
, parent_method()
from ParentClass
, and child_method()
from ChildClass
. This demonstrates multilevel inheritance, where the ChildClass
inherits attributes and methods from the ParentClass
, which itself inherits from the GrandparentClass
.
Hierarchical Inheritance
Hierarchical inheritance is a form of inheritance in object-oriented programming, where multiple child classes inherit attributes and methods from a single parent class. This creates a tree-like structure of classes, allowing a single parent class to share its functionalities and characteristics with multiple child classes. In Python, hierarchical inheritance can be implemented using the following approach:
- Define the Parent Class: First, create the parent class with its attributes and methods. This class will serve as the base class for multiple child classes.
class ParentClass:
def __init__(self, attribute1):
self.attribute1 = attribute1
def parent_method(self):
print("This is a method in the parent class.")
- Define the Child Classes: Next, create two or more child classes by including the name of the parent class in parentheses right after each child class name. This indicates that each child class will inherit from the same parent class.
class ChildClass1(ParentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def child1_method(self):
print("This is a method in ChildClass1.")
class ChildClass2(ParentClass):
def __init__(self, attribute1, attribute3):
super().__init__(attribute1)
self.attribute3 = attribute3
def child2_method(self):
print("This is a method in ChildClass2.")
- Create Objects and Test Inheritance: Now, you can create instances of the parent and child classes, and verify that each child class has access to the parent class’s attributes and methods.
parent_object = ParentClass("value1")
parent_object.parent_method()
child1_object = ChildClass1("value1", "value2")
child1_object.parent_method()
child1_object.child1_method()
child2_object = ChildClass2("value1", "value3")
child2_object.parent_method()
child2_object.child2_method()
In this example, both child1_object
and child2_object
have access to parent_method()
from ParentClass
as well as their respective child1_method()
and child2_method()
. This demonstrates hierarchical inheritance, where multiple child classes, ChildClass1
and ChildClass2
, inherit attributes and methods from a single parent class, ParentClass
.
Hybrid Inheritance
Hybrid inheritance is a combination of different types of inheritance, such as single, multiple, multilevel, and hierarchical inheritance, in a single program. This allows you to create more complex relationships between classes, promoting code reusability, modularity, and flexibility. In Python, hybrid inheritance can be implemented using the following approach:
- Define the Parent and Grandparent Classes: First, create the parent and grandparent classes with their attributes and methods.
class GrandparentClass:
def grandparent_method(self):
print("This is a method in the grandparent class.")
class ParentClass1(GrandparentClass):
def parent1_method(self):
print("This is a method in ParentClass1.")
class ParentClass2(GrandparentClass):
def parent2_method(self):
print("This is a method in ParentClass2.")
In this example, we have a grandparent class and two parent classes, where both parent classes inherit from the grandparent class. This is a combination of hierarchical and multilevel inheritance.
- Define the Child Classes: Next, create the child classes by including the names of the parent classes in parentheses, separated by commas, right after the child class name. This indicates that the child class will inherit from multiple parent classes.
class ChildClass(ParentClass1, ParentClass2):
def child_method(self):
print("This is a method in the child class.")
In this example, the ChildClass
inherits from both ParentClass1
and ParentClass2
, which in turn inherit from GrandparentClass
. This creates a hybrid inheritance structure, combining multiple inheritance with multilevel and hierarchical inheritance.
- Create Objects and Test Inheritance: Now, you can create instances of the grandparent, parent, and child classes, and verify that the child class has access to the attributes and methods of all parent and grandparent classes.
grandparent_object = GrandparentClass()
grandparent_object.grandparent_method()
parent1_object = ParentClass1()
parent1_object.grandparent_method()
parent1_object.parent1_method()
parent2_object = ParentClass2()
parent2_object.grandparent_method()
parent2_object.parent2_method()
child_object = ChildClass()
child_object.grandparent_method()
child_object.parent1_method()
child_object.parent2_method()
child_object.child_method()
In this example, the child_object
has access to grandparent_method()
from GrandparentClass
, parent1_method()
from ParentClass1
, parent2_method()
from ParentClass2
, and child_method()
from ChildClass
. This demonstrates hybrid inheritance, where the ChildClass
inherits attributes and methods from multiple parent classes, which themselves inherit from a single grandparent class.
Creating Parent and Child Classes
Creating parent and child classes in Python involves defining a base class (parent) and a derived class (child) that inherits the properties and methods of the base class. This process allows for better code reusability, organization, and maintenance.
To create parent and child classes in Python, follow these steps:
- Define the Parent Class: First, create the parent class with its attributes and methods. This class will serve as the base class for the child class.
class ParentClass:
def __init__(self, attribute1):
self.attribute1 = attribute1
def parent_method(self):
print("This is a method in the parent class.")
In the example above, we have defined a ParentClass
with an attribute attribute1
and a method parent_method()
.
- Define the Child Class: Next, create the child class by including the parent class’s name in parentheses right after the child class name. This indicates that the child class will inherit from the parent class.
class ChildClass(ParentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def child_method(self):
print("This is a method in the child class.")
In this example, we have defined a ChildClass
that inherits from ParentClass
. The super().__init__(attribute1)
call in the ChildClass
constructor is used to initialize the parent class’s attributes. The child class also has its own attribute attribute2
and a method child_method()
.
- Create Objects and Test Inheritance: Now, you can create instances of the parent and child classes, and verify that the child class has access to the parent class’s attributes and methods.
parent_object = ParentClass("value1")
parent_object.parent_method()
child_object = ChildClass("value1", "value2")
child_object.parent_method()
child_object.child_method()
In this example, the child_object
has access to both parent_method()
from ParentClass
and child_method()
from ChildClass
. This demonstrates the creation of parent and child classes in Python, where the ChildClass
inherits attributes and methods from the ParentClass
.
Overriding Methods in Child Classes
Overriding methods in child classes is a technique used in object-oriented programming that allows you to modify or replace a method inherited from a parent class with a new implementation in the child class. This enables the child class to have its own version of the inherited method, providing more specific or different behavior.
To override methods in child classes in Python, follow these steps:
- Define the Parent Class: First, create the parent class with its attributes and methods. This class will serve as the base class for the child class.
class ParentClass:
def __init__(self, attribute1):
self.attribute1 = attribute1
def method_to_override(self):
print("This is a method in the parent class.")
In the example above, we have defined a ParentClass
with an attribute attribute1
and a method method_to_override()
.
- Define the Child Class: Next, create the child class by including the parent class’s name in parentheses right after the child class name. This indicates that the child class will inherit from the parent class.
class ChildClass(ParentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def method_to_override(self):
print("This is a method in the child class.")
In this example, we have defined a ChildClass
that inherits from ParentClass
. To override the method_to_override()
, simply define a new method with the same name in the child class. In this case, the ChildClass
now has its own version of method_to_override()
.
- Create Objects and Test Method Overriding: Now, you can create instances of the parent and child classes, and verify that the child class uses its own version of the overridden method.
parent_object = ParentClass("value1")
parent_object.method_to_override()
child_object = ChildClass("value1", "value2")
child_object.method_to_override()
In this example, calling method_to_override()
on the parent_object
will execute the method from the ParentClass
, while calling it on the child_object
will execute the overridden method from the ChildClass
.
Using the Super() Function
The super()
function in Python is used to call a method from a parent class in a derived class (child class). This allows you to reuse and extend the behavior of the parent class without explicitly referring to its name. It is commonly used in constructors to initialize the parent class’s attributes and methods, but it can also be used to call any method from the parent class.
Here’s how to use the super()
function in Python:
- Define the Parent Class: First, create the parent class with its attributes and methods. This class will serve as the base class for the child class.
class ParentClass:
def __init__(self, attribute1):
self.attribute1 = attribute1
def parent_method(self):
print("This is a method in the parent class.")
In the example above, we have defined a ParentClass
with an attribute attribute1
and a method parent_method()
.
- Define the Child Class: Next, create the child class by including the parent class’s name in parentheses right after the child class name. This indicates that the child class will inherit from the parent class.
class ChildClass(ParentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def child_method(self):
print("This is a method in the child class.")
In this example, we have defined a ChildClass
that inherits from ParentClass
. To initialize the parent class’s attributes, we use the super().__init__(attribute1)
call in the ChildClass
constructor. This allows the child class to call the constructor of the parent class and pass the required arguments.
- Call Parent Class Methods with
super()
: You can also use thesuper()
function to call other methods from the parent class within the child class.
class ChildClass(ParentClass):
def __init__(self, attribute1, attribute2):
super().__init__(attribute1)
self.attribute2 = attribute2
def child_method(self):
print("This is a method in the child class.")
super().parent_method()
In this example, the child_method()
in ChildClass
calls the parent_method()
from ParentClass
using super().parent_method()
.
- Create Objects and Test the
super()
Function: Now, you can create instances of the parent and child classes, and verify that thesuper()
function correctly calls the parent class’s methods.
parent_object = ParentClass("value1")
parent_object.parent_method()
child_object = ChildClass("value1", "value2")
child_object.child_method()
In this example, calling child_method()
on the child_object
will print “This is a method in the child class.” followed by “This is a method in the parent class.” due to the super().parent_method()
call.
The super()
function simplifies inheritance in Python by allowing you to call parent class methods without explicitly referring to the parent class name, making your code more maintainable and less prone to errors.
Method Resolution Order (MRO)
In Python, the Method Resolution Order (MRO) is the order in which the interpreter looks for a method or an attribute in the hierarchy of classes. MRO is essential in cases of multiple inheritance, where a class can inherit from more than one parent class, leading to possible ambiguity in determining which parent class’s method should be called.
Python uses the C3 Linearization algorithm (also known as C3 superclass linearization) to determine the MRO. This algorithm ensures that the MRO respects both local precedence ordering (the order in which parent classes are listed) and monotonicity (the MRO of a class must be consistent with the MRO of its parents).
You can find the MRO of a class using the mro()
method or the __mro__
attribute:
- Define the classes: Let’s consider an example with multiple inheritance:
class A:
def method(self):
print("Method in class A")
class B(A):
def method(self):
print("Method in class B")
class C(A):
def method(self):
print("Method in class C")
class D(B, C):
pass
Here, class D
inherits from classes B
and C
, which both inherit from class A
.
- Check the MRO: To check the MRO of class
D
, you can use themro()
method:
print(D.mro())
This will output:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
Alternatively, you can use the __mro__
attribute:
print(D.__mro__)
This will output the same result:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
In this example, the MRO for class D
is D, B, C, A, and finally the built-in object
class. This means that when calling a method on an instance of class D
, Python will first look for the method in class D
, then in class B
, then in class C
, and finally in class A
.
Composition vs. Inheritance
Both composition and inheritance are techniques used in object-oriented programming to establish relationships between classes, promoting code reusability, organization, and maintenance. While they share some similarities, they serve different purposes and have their own advantages and drawbacks.
Inheritance:
- Inheritance is a technique where a class (child class) inherits the attributes and methods of another class (parent class). It establishes an “is-a” relationship between the parent and child classes, indicating that the child class is a subtype of the parent class.
- Inheritance allows you to create a new class based on an existing one, with the new class automatically inheriting all the parent class’s attributes and methods. You can then extend or override the inherited methods to provide specific functionality.
- Inheritance promotes code reusability, as you can create new classes without writing the entire code from scratch. It also ensures consistency across related classes.
- Drawbacks of inheritance include the risk of creating tightly-coupled classes, which can make code harder to maintain and understand. Moreover, multiple inheritance can lead to ambiguity and complexity in determining the method resolution order.
Composition:
- Composition is a technique where a class (composite class) contains an instance of another class (component class) as an attribute. It establishes a “has-a” or “part-of” relationship between the composite and component classes, indicating that the composite class is made up of one or more instances of the component class.
- Composition allows you to create a new class by combining instances of other classes, which can then be used as building blocks to provide specific functionality. You can delegate functionality to the component instances by calling their methods.
- Composition promotes code modularity, flexibility, and maintainability, as changes to a component class do not directly impact other composite classes. It also enables you to create complex systems by combining simple components.
- Drawbacks of composition include the potential for increased complexity when managing relationships between many component classes and the need to write more boilerplate code to delegate functionality to the component instances.
When to choose composition over inheritance:
- When you need to model a “has-a” or “part-of” relationship between classes, rather than an “is-a” relationship.
- When you want to promote modularity, flexibility, and maintainability in your code by minimizing the coupling between classes.
- When you want to create complex systems by combining simple components or when you need to reuse a class in multiple unrelated classes.
In general, it’s a good practice to use composition over inheritance, as it provides greater flexibility and modularity. However, there are cases where inheritance is a more suitable option, especially when you need to model a clear “is-a” relationship or when you need to reuse a significant amount of code across related classes. The choice between composition and inheritance will depend on the specific requirements and structure of your program.