πŸ˜‰Inheritance

nheritance is a fundamental concept in object-oriented programming (OOP) that allows a class to inherit the properties and methods of another class. In Python, inheritance enables us to create a new class (child class) that is a modified or specialized version of an existing class (parent class).

The child class can access all the attributes and methods of the parent class, and it can also override or extend those methods to add new functionality. This makes inheritance a powerful mechanism for code reuse and allows us to build complex applications with ease.

For example, imagine we have a class called Animal that defines basic attributes and methods for all animals, such as name, age, and eat(). We can create a child class called Dog that inherits from Animal and adds its own unique attributes and methods, such as breed and bark().

Inheritance in Python is implemented using the keyword class, followed by the child class name and the name of the parent class in parentheses. Here's an example:

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print(f"{self.name} is eating.")

class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed

    def bark(self):
        print("Woof!")

In this example, the Dog class inherits from the Animal class and adds a new attribute breed and a new method bark(). The super() function is used to call the constructor of the parent class and initialize its attributes.

Types of Inheritance

In Python, there are several types of inheritance that you can use to create a child class. The most common types of inheritance are:

  1. Single inheritance: In single inheritance, a child class inherits from a single parent class. This is the simplest and most common type of inheritance.

  2. Multiple inheritance: In multiple inheritance, a child class inherits from two or more parent classes. This allows you to combine the features of multiple classes into a single class.

  3. Multi-level inheritance: In multi-level inheritance, a child class inherits from a parent class, which in turn inherits from another parent class. This creates a hierarchical structure of classes.

  4. Hierarchical inheritance: In hierarchical inheritance, multiple child classes inherit from a single parent class. This allows you to create a set of related classes with a common base class.

  5. Hybrid inheritance: Hybrid inheritance is a combination of two or more types of inheritance. For example, you can use multiple inheritance along with multi-level inheritance to create a complex class hierarchy.

Each type of inheritance has its own advantages and disadvantages, and the choice of which type to use depends on the specific requirements of your application.

Inheritance Syntax

In Python, inheritance is implemented using the class keyword, followed by the name of the child class and the name of the parent class in parentheses. Here's the syntax for creating a child class that inherits from a parent class:

class ParentClass:
    # Parent class attributes and methods

class ChildClass(ParentClass):
    # Child class attributes and methods

In this syntax, the ParentClass is the name of the parent class that the ChildClass is inheriting from. All the attributes and methods of the parent class are automatically inherited by the child class.

To access the parent class attributes and methods from the child class, you can use the super() function. The super() function returns a temporary object of the superclass, which allows you to call its methods.

Here's an example of inheritance in Python:

class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f"{self.name} is eating.")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed

    def bark(self):
        print("Woof!")

In this example, the Dog class is inheriting from the Animal class. The super() function is used to call the constructor of the parent class and initialize its attributes.

Example : Python Inheritance

Here's an example of how to use inheritance in Python to create a child class that inherits from a parent class:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        print(f"Hi, my name is {self.name} and I'm {self.age} years old.")

class Student(Person):
    def __init__(self, name, age, major):
        super().__init__(name, age)
        self.major = major

    def introduce(self):
        super().introduce()
        print(f"I'm studying {self.major} at the university.")

In this example, the Person class defines an __init__() method that initializes the name and age attributes and a introduce() method that introduces the person.

The Student class is a child class that inherits from the Person class. It adds a new attribute major and overrides the introduce() method to include information about the major.

The super() function is used to call the constructor of the parent class and initialize its attributes. It is also used to call the introduce() method of the parent class from within the introduce() method of the child class.

Here's an example of how to use these classes:

person = Person("John", 25)
person.introduce()

student = Student("Jane", 20, "Computer Science")
student.introduce()

Output:

Hi, my name is John and I'm 25 years old.
Hi, my name is Jane and I'm 20 years old.
I'm studying Computer Science at the university.

As you can see, the Student class inherits the introduce() method from the Person class and adds its own functionality to it. This is an example of how inheritance can be used to reuse code and build more complex classes from simpler ones.

Accessing Parent Class from Child Class

In Python, you can access the attributes and methods of the parent class from a child class using the super() function. The super() function returns a temporary object of the parent class, which allows you to call its methods and access its attributes.

Here's an example of how to access the parent class from a child class:

class Parent:
    def __init__(self, name):
        self.name = name

    def greeting(self):
        print(f"Hello, my name is {self.name}.")

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name)
        self.age = age

    def greeting(self):
        super().greeting()
        print(f"I'm {self.age} years old.")

In this example, the Child class is inheriting from the Parent class. It overrides the greeting() method to include the age of the child.

The super() function is used to call the greeting() method of the parent class and include its output in the child class's greeting. This allows the child class to reuse the code of the parent class while adding its own functionality.

Here's an example of how to use these classes:

parent = Parent("John")
parent.greeting()

child = Child("Jane", 10)
child.greeting()

Output:

Hello, my name is John.
Hello, my name is Jane.
I'm 10 years old.

As you can see, the Child class is able to access the name attribute of the parent class and call its greeting() method using the super() function. This allows it to reuse the code of the parent class while adding its own functionality.

Overriding Methods in Child Class

In Python, when a child class inherits from a parent class, it can override the parent class's methods with its own implementation. This is called method overriding.

To override a method in the child class, you simply define a method with the same name in the child class. When the method is called on an instance of the child class, the child class's implementation of the method will be used instead of the parent class's implementation.

Here's an example of method overriding in Python:

class Parent:
    def say_hello(self):
        print("Hello from Parent class!")

class Child(Parent):
    def say_hello(self):
        print("Hello from Child class!")

parent = Parent()
parent.say_hello()  # Output: Hello from Parent class!

child = Child()
child.say_hello()  # Output: Hello from Child class!

In this example, the Child class is inheriting from the Parent class and overriding its say_hello() method with its own implementation. When the say_hello() method is called on an instance of the Child class, the child class's implementation is used instead of the parent class's implementation.

Note that when you override a method in the child class, the parent class's implementation is still available by using the super() function. Here's an example:

class Parent:
    def say_hello(self):
        print("Hello from Parent class!")

class Child(Parent):
    def say_hello(self):
        super().say_hello()
        print("Hello from Child class!")

child = Child()
child.say_hello()  # Output: Hello from Parent class! Hello from Child class!

In this example, the Child class is using the super() function to call the say_hello() method of the parent class, and then adding its own implementation. This allows the child class to reuse the code of the parent class while adding its own functionality.

Using super() function in Child Class

In Python, the super() function is used to call a method from the parent class in a child class. It is often used in conjunction with method overriding to reuse code from the parent class while adding additional functionality in the child class.

Here's an example of using the super() function in a child class:

class Parent:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f"Hello, my name is {self.name}.")

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name)
        self.age = age

    def say_hello(self):
        super().say_hello()
        print(f"I'm {self.age} years old.")

child = Child("Alice", 10)
child.say_hello()  # Output: Hello, my name is Alice. I'm 10 years old.

In this example, the Child class is inheriting from the Parent class and overriding its say_hello() method with its own implementation. The super() function is used to call the say_hello() method of the parent class and then add the child class's implementation. This allows the child class to reuse the code of the parent class while adding its own functionality.

Note that in the __init__() method of the Child class, the super() function is also used to call the __init__() method of the parent class. This ensures that the parent class's __init__() method is called before the child class's __init__() method, and that the name attribute is properly initialized.

Multiple Inheritance

Multiple inheritance is a feature of object-oriented programming where a subclass can inherit from multiple parent classes. In Python, you can achieve multiple inheritance by specifying multiple parent classes in the class definition separated by commas.

Here's an example of multiple inheritance in Python:

class Parent1:
    def method1(self):
        print("Method 1 from Parent 1")

class Parent2:
    def method2(self):
        print("Method 2 from Parent 2")

class Child(Parent1, Parent2):
    def method3(self):
        print("Method 3 from Child")

child = Child()
child.method1()  # Output: Method 1 from Parent 1
child.method2()  # Output: Method 2 from Parent 2
child.method3()  # Output: Method 3 from Child

In this example, the Child class is inheriting from both the Parent1 and Parent2 classes. It can access the methods of both parent classes using dot notation.

When a class inherits from multiple parent classes, it can override methods from both parent classes. If both parent classes have a method with the same name, the method in the first parent class listed in the inheritance statement will be called. If the child class needs to call a specific method from a specific parent class, it can do so using the super() function along with the parent class's name.

Here's an example of using the super() function to call a method from a specific parent class in a multiple inheritance scenario:

class Parent1:
    def method(self):
        print("Method from Parent 1")

class Parent2:
    def method(self):
        print("Method from Parent 2")

class Child(Parent1, Parent2):
    def method(self):
        super(Parent1, self).method()

child = Child()
child.method()  # Output: Method from Parent 1

In this example, the Child class is inheriting from both the Parent1 and Parent2 classes, both of which have a method named method(). The super() function is used to call the method() of Parent1, so the output will be "Method from Parent 1".

Method Resolution Order (MRO)

Method Resolution Order (MRO) is the order in which Python looks for methods in a class hierarchy. When a method is called on an instance of a class, Python first looks for the method in the instance's class. If the method is not found in the instance's class, Python looks for the method in the class's parent classes, following the MRO.

In Python, the MRO is determined using the C3 linearization algorithm, which is a special algorithm designed to create a linear order of inheritance hierarchy.

You can use the mro() method to get the MRO for a class. The mro() method returns a tuple that contains the class itself followed by the classes it inherits from, in the order that they will be searched for methods.

Here's an example of using the mro() method to get the MRO for a class:

class Parent1:
    pass

class Parent2:
    pass

class Child(Parent1, Parent2):
    pass

print(Child.mro())  # Output: [<class '__main__.Child'>, <class '__main__.Parent1'>, <class '__main__.Parent2'>, <class 'object'>]

In this example, the Child class is inheriting from both the Parent1 and Parent2 classes. The mro() method is used to get the MRO for the Child class, which is a tuple that contains the Child class itself followed by the Parent1 class, the Parent2 class, and finally the built-in object class.

Understanding the MRO is important when dealing with multiple inheritance, as it determines the order in which Python will search for methods in the class hierarchy.

Diamond Problem in Multiple Inheritance

The diamond problem is a common issue that arises in multiple inheritance when two parent classes of a child class inherit from the same base class. This results in the diamond shape of the class hierarchy, where the child class is at the bottom of the diamond and there are two paths to the base class.

Consider the following example:

class A:
    def method(self):
        print("Method from A")

class B(A):
    pass

class C(A):
    def method(self):
        print("Method from C")

class D(B, C):
    pass

d = D()
d.method()  # Output: Method from C

In this example, the D class inherits from both B and C, which in turn both inherit from the A class. Both B and C have a method named method(), and when D calls the method() method, Python follows the MRO to look for the method in the class hierarchy. The MRO for D is [D, B, C, A, object], which means that Python will first look for the method() method in the B class, then the C class, then the A class, and finally the object class.

In this case, Python finds the method() method in the C class first, and so it is called. This means that the method() method of A is never called, which can cause unexpected behavior if C is intended to override A.

To avoid the diamond problem, it's important to carefully design class hierarchies and avoid multiple inheritance where possible. When multiple inheritance is necessary, it's important to understand the MRO and the order in which methods will be searched for in the class hierarchy. You can also use the super() function to explicitly call methods in parent classes and avoid unintended method overrides.

Last updated