Python Object Inheritance

Click to share! ⬇️

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.

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.

  1. 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
  1. 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
  1. 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
  1. 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:

  1. 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
  1. 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
  1. 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
  1. 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:

  1. 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.")
  1. 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.

  1. 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:

  1. 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.")
  1. 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.

  1. 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:

  1. 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.")
  1. 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.")
  1. 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.")
  1. 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:

  1. 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.")
  1. 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.")
  1. 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:

  1. 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.

  1. 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.

  1. 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:

  1. 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().

  1. 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().

  1. 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:

  1. 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().

  1. 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().

  1. 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:

  1. 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().

  1. 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.

  1. Call Parent Class Methods with super(): You can also use the super() 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().

  1. Create Objects and Test the super() Function: Now, you can create instances of the parent and child classes, and verify that the super() 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:

  1. 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.

  1. Check the MRO: To check the MRO of class D, you can use the mro() 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:

  1. 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.
  2. 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.
  3. Inheritance promotes code reusability, as you can create new classes without writing the entire code from scratch. It also ensures consistency across related classes.
  4. 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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:

  1. When you need to model a “has-a” or “part-of” relationship between classes, rather than an “is-a” relationship.
  2. When you want to promote modularity, flexibility, and maintainability in your code by minimizing the coupling between classes.
  3. 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.

Click to share! ⬇️