top

Search

Python Tutorial

.

UpGrad

Python Tutorial

Inheritance in Python

Introduction

Python supports inheritance, which allows new classes to inherit properties and methods from other existing classes. It encourages code reuse and strengthens a program's general framework. Any Python developer can comprehend inheritance since it works with the improvement of successful and secluded code.

What is Inheritance in Python?

Inheritance is a critical concept in Python and other object-oriented programming languages. It empowers a class to acquire characteristics and capabilities from an alternate class, referred to as the parent or base class. The term "child" or "derived class" refers to the class that inherits certain traits and methods.

Read the example below for a better understanding:

Assume we have an "Animal" class with properties like "name" and "age" as well as methods like "eat()" and "sleep()". Using inheritance in Python, we can construct a derived class named "Dog" that has all of the "Animal" class's attributes and methods, as well as the ability to add new ones like "bark()" and "fetch()". 

What is MRO?

The expression "MRO" means "Method Resolution Order," and it depicts how a programming language resolves conflicts between classes that have a similar strategy name. Understanding this thought is fundamental since it impacts how the proper strategy execution is chosen while calling a technique in an article.

Python's C3 linearization procedure is utilized to compute MRO. A class' MRO is determined by considering the depth-first search (DFS) cross of its inheritance progressive system while keeping up with the parent classes' request for show.

The MRO makes sure that each technique in a class order will be called just one time.

Why is Inheritance Used?

Python involves inheritance for various purposes, including:

1. Code Reusability: We can reuse code from previous classes thanks to inheritance, which wipes out the need to make similar code repeatedly. This supports code that is clearer and more compelling.

2. Modularity: By breaking down code into specific, individual classes, inheritance empowers seclusion. The attributes and strategies of each parent class are acquired by each inferred class, which works on the program's general structure.

3. Polymorphism: Polymorphism, which is the ability of objects of unmistakable classes to respond in different ways to a similar technique or characteristic, is made conceivable through inheritance. Since different items can show assorted ways of behaving while still adjusting to a uniform point of interaction, writing computer programs is therefore more versatile and dynamic.

Python Inheritance Syntax

The language structure for inheritance in Python is essential. The accompanying linguistic structure is utilized to make a get class that gets from a base class:

class DerivedClassName(BaseClassName):

    # The inferred class-specific properties and techniques

Here is an example of the syntax:

class Animal:

  def __init__(self, name, age):

    self.name = name

    self.age = age

  def eat(self):

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

  def sleep(self):

    print(f"{self.name} is sleeping.")

class Dog(Animal):

  def bark(self):

    print(f"{self.name} is barking.")

dog = Dog("Dog", 2) 

dog.eat()

dog.sleep()

dog. Bark()

Code:

class DerivedClassName(BaseClassName):

    # The inferred class-specific properties and techniques

Here is an example of the syntax:

class Animal:

  def __init__(self, name, age):

    self.name = name

    self.age = age

  def eat(self):

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

  def sleep(self):

    print(f"{self.name} is sleeping.")

class Dog(Animal):

  def bark(self):

    print(f"{self.name} is barking.")

dog = Dog("Dog", 2) 

dog.eat()

dog.sleep()

dog. Bark()

Output:

"Dog is eating."

"Dog is sleeping."

"Dog is barking."

The essential class in the above case is designated "Animal," and it has the properties "name" and "age," as well as the techniques "eat()" and "sleep()." ' class Dog(Animal):' is the grammar used to show that the inferred class 'Canine' is an inheritor of the class 'Animal'; 'bark()' is a further capability that it has. The derived class instantly receives access to the base class's properties and methods since it is descended from it.

Making a Child Class

To create a class that inherits functionality from another class, specify the parent class as the base class of the child class. The following methods should be continued to lay out a child class:

1. Indicate the Child Class: To begin, make another class with a particular name.

class ChildClassName(ParentClassName):

Code:

class ChildClassName(ParentClassName):

    # The child class-specific properties and techniques

2. Inherit from the Parent Class: Recognize the parent or base class that you wish to acquire from inside the bracket of the child class announcement.

class ChildClassName(ParentClassName):

Code:

class ChildClassName(ParentClassName):

    # The child class-specific properties and techniques

3. Incorporate Any Extra Attributes or Strategies That Are Specific to the Child Class: After referring to the parent class, you can incorporate some other qualities or strategies that are specific to the child class.

Here is a case to show the methodology:

class Person:

    def __init__(self, name, age):

        self.name = name

        self.age = age

    def greet(self):

        print(f"Hello, my name is {self.name}. I'm {self.age} years old.")

class Student(Person):

    def __init__(self, name, age, student_id):

        super().__init__(name, age)

        self.student_id = student_id

    def study(self):



        print(f"I am {self.name}, a student with ID {self.student_id}. I am studying hard!")



# Generating an example of the Student class

student1 = Student("John", 20, "12345")



# Accessing attributes and methods from the parent class

print(student1.name) # Output: John

print(student1.age) # Output: 20

student1.greet() # Output: Hello, my name is John. I am 20 years old.

Code:

class Person:

    def __init__(self, name, age):

        self.name = name

        self.age = age

    def greet(self):

        print(f"Hello, my name is {self.name}. I'm {self.age} years old.")

class Student(Person):

    def __init__(self, name, age, student_id):

        super().__init__(name, age)

        self.student_id = student_id

    def study(self):



        print(f"I am {self.name}, a student with ID {self.student_id}. I am studying hard!")



# Generating an example of the Student class

student1 = Student("John", 20, "12345")



# Accessing attributes and methods from the parent class

print(student1.name) # Output: John

print(student1.age) # Output: 20

student1.greet() # Output: Hello, my name is John. I am 20 years old.

In Python, the superclass (or parent class) of a child class is referred to utilizing the 'super()' strategy. It empowers the child class to get to and acquire the parent class' strategies and properties.

In the model given, we are conjuring the constructor of the individual class inside the understudy class' '__init__' capability by utilizing 'super().__init__(name, age)'. This states the understudy item's name and age properties, which were acquired from the person class.

The 'super()' addresses that parent class introductions are finished before continuing to instate the child class. To take into consideration a suitable inheritance, this ensures that the child class approaches the parent class properties and strategies.

We can supply the name and age boundaries to the constructor of the individual class by calling 'super().__init__(name, age)'. This ensures that the understudy item's name and age fields are appropriately populated.

Without utilizing "super()," we would need to copy the code and present issues by physically instating the name and age properties in the understudy class. The inheritance method is simplified by utilizing 'super()'.

What Does a Python Object Class Mean? 

An item class in Python acts as a model or layout for building objects. It indicates the properties and tasks that objects of that class will uphold.

As an illustration, think about the class "Car":

class Car:

    def __init__(self, brand, model):

        self.brand = brand

        self.model = model

    

    def start_engine(self):

        print("Engine started!")

    

    def drive(self):

        print(f"Driving the {self.brand} {self. Model}.")

Code:

class Car:

    def __init__(self, brand, model):

        self.brand = brand

        self.model = model

    

    def start_engine(self):

        print("Engine started!")

    

    def drive(self):

        print(f"Driving the {self.brand} {self. Model}.")

The "Car" class in this representation has strategies like "start_engine()" and "drive()," as well as properties like "brand" and "model." These characteristics and procedures indicate how a car acts and what it is like.

You can play out the following to make an example of the vehicle class and utilize its properties and techniques:

car1 = Car("Toyota", "Camry")

print(car1.brand) # Result: Toyota

print(car1.model) # Result: Camry

car1.start_engine() # Result: Motor turned over!

car1.drive() # Result: Driving the Toyota Camry.

Code:

car1 = Car("Toyota", "Camry")

print(car1.brand) # Result: Toyota

print(car1.model) # Result: Camry

car1.start_engine() # Result: Motor turned over!

car1.drive() # Result: Driving the Toyota Camry.

In this model, we fabricate a "car1"- class case of the "Car" class. While developing the item, we supply boundaries that incorporate the boundaries for the brand and model properties. Speck documentation helps achieve these attributes, as confirmed by the result. The class' techniques can likewise be called utilizing spot documentation.

While the "drive()" capability distributes a message demonstrating which vehicle is being driven, the "start_engine()" capability shows a reaction showing that the motor has turned over.

Different Types of Inheritance in Python

To build subclasses in Python, many forms of inheritance can be employed. Let's look at a few instances:

1. Single Inheritance:

class Animal:

    def __init__(self, name):

        self.name = name

    

    def sound(self):

        pass

class Dog(Animal):

    def sound(self):

        return "Woof!"

dog = Dog("Max")

print(dog.sound())  # Output: Woof!

Code:

class Animal:

    def __init__(self, name):

        self.name = name

    

    def sound(self):

        pass

class Dog(Animal):

    def sound(self):

        return "Woof!"

dog = Dog("Max")

print(dog.sound())  # Output: Woof!

In this illustration, we have an "Animal" parent class and a "Dog" subclass. The parent class' attributes and techniques are passed down to the subclass. To return the sound that a canine creates, the "Dog" class changes the parent class' "sound()" capability.

2. Various Inheritances:

class Flyer:

    def fly(self):

        print("I can fly!")

class Swimmer:

    def swim(self):

        print("I can swim!")

class Duck(Flyer, Swimmer):

    pass

duck = Duck()

duck.fly() # Result: I can fly!

duck.swim() # Result: I can swim!

Code:

class Flyer:

    def fly(self):

        print("I can fly!")

class Swimmer:

    def swim(self):

        print("I can swim!")

class Duck(Flyer, Swimmer):

    pass

duck = Duck()

duck.fly() # Result: I can fly!

duck.swim() # Result: I can swim!

In the above case, "Flyer" and "Swimmer" are the two parent classes, while "Duck" is a subclass. The subclass acquires strategies from both parent classes. There are no additional strategies or properties for the "Duck" class.

A condition wherein at least two parent classes of a subclass share a technique and the subclass gets it from both parent classes is alluded to as the "jewel issue." While summoning the normal strategy from the subclass, this might prompt disarray.

The "fly" strategy is available in both the "Flyer" and "Swimmer" classes in the above piece of code. The "Duck" class inherits both the parent classes. Since it isn't evident which form of the "fly" strategy ought to be utilized, utilizing the "fly" technique on an occasion of the "Duck" class would bring about uncertainty. This is known as the diamond issue.

Python utilizes the Method Resolution Order (MRO), which lays out the grouping in which classes are looked for strategies, to resolve this issue. On account of the "Duck" class, the strategy from "Flyer" will take precedence over the technique from "Swimmer" since it is determined first in the enclosure during class definition.

The "fly" strategy from the "Flyer" class will be utilized when the "fly" technique is approached as a "Duck" class example. The super() capability, which empowers us to get to strategies from parent classes, might be utilized to definitively conjure the "fly" strategy from the "Swimmer" class.

The precious stone issue arises when a subclass inherits from a few parent classes using a common technique.

3. Multilevel Inheritance:

class Animal:

    def eat(self):

        print("I can eat!")

class Mammal(Animal):

    def sleep(self):

        print("I can sleep!")

class Dolphin(Mammal):

    def swim(self):

        print("I can swim!")

dolphin = Dolphin()

dolphin.eat() # Result: I can eat!

dolphin.sleep() #Output: I can sleep!

dolphin.Swim() # Result: I can swim!

Code:

class Animal:

    def eat(self):

        print("I can eat!")

class Mammal(Animal):

    def sleep(self):

        print("I can sleep!")

class Dolphin(Mammal):

    def swim(self):

        print("I can swim!")

dolphin = Dolphin()

dolphin.eat() # Result: I can eat!

dolphin.sleep() #Output: I can sleep!

dolphin.Swim() # Result: I can swim!

We have three characterizations in this outline: "Animal," "Mammal,"  and "Dolphin." An inheritance level is addressed by each class. The "Dolphin" class acquires functionality from the "Mammal" and "Animal" classes'.

4. Hierarchical Inheritance in Python:

class Vehicle:

    def drive(self):

        print("I can drive!")

class Car(Vehicle):

    def honk(self):

        print("Honk honk!")

class Motorcycle(Vehicle):

    def rev_engine(self):

        print("Vroom vroom!")

car = Car()

car.drive()  # Output: I can drive!

car.honk()  # Output: Honk honk!

motorcycle = Motorcycle()

motorcycle.drive()  # Output: I can drive!

motorcycle.rev_engine()  # Output: Vroom vroom!

Code:

class Vehicle:

    def drive(self):

        print("I can drive!")

class Car(Vehicle):

    def honk(self):

        print("Honk honk!")

class Motorcycle(Vehicle):

    def rev_engine(self):

        print("Vroom vroom!")

car = Car()

car.drive()  # Output: I can drive!

car.honk()  # Output: Honk honk!

motorcycle = Motorcycle()

motorcycle.drive()  # Output: I can drive!

motorcycle.rev_engine()  # Output: Vroom vroom!

In the above instance, "Vehicle" fills in as the parent class, and "Car" and "Motorcycle" act as the subclasses. The parent class' property and strategy are passed down to the two subclasses. Every subclass likewise has its special methodology.

5. Hybrid Inheritance in Python:

class Animal:

    def eat(self):

        print("I can eat!")

class Bird(Animal):

    def fly(self):

        print("I can fly!")

class Fish(Animal):

    def swim(self):

        print("I can swim!")

class Penguin(Bird, Fish):

    pass

penguin = Penguin()

penguin.eat()  # Output: I can eat!

penguin.fly()  # Output: I can fly!

penguin.swim()  # Output: I can swim!

Code:

class Animal:

    def eat(self):

        print("I can eat!")

class Bird(Animal):

    def fly(self):

        print("I can fly!")

class Fish(Animal):

    def swim(self):

        print("I can swim!")

class Penguin(Bird, Fish):

    pass

penguin = Penguin()

penguin.eat()  # Output: I can eat!

penguin.fly()  # Output: I can fly!

penguin.swim()  # Output: I can swim!

The four classes in this model are "Animal," "Bird," "Fish," and "Penguin." A hybrid inheritance is made between the "Penguin" class and the "Bird" and "Fish" classes. All in all, the penguin can eat, fly, and swim since it has the qualities of both a bird and a fish.

Conclusion

All in all, inheritance is a powerful tool in object-oriented programming; however, it is critical to consider its drawbacks. These drawbacks include close coupling, limited flexibility, the fragile base class problem, a lack of exemplification, and limited code reuse.

It is fundamental to completely look at the framework's necessities and consider substitute plan designs, for example, synthesis or interfaces, to reduce these limitations. By doing this, we can fabricate a codebase that is not so much reliant on but rather more versatile for evolving necessities.

FAQs

1. What is the issue with the delicate base class?

At the point when changes to a superclass unanticipatedly affect its subclasses, they could obliterate those subclasses' usefulness. This is known as the delicate base class issue.

2. How could inheritance conflict with the exemplification guideline?

By empowering direct access to private and public components, inheritance disregards the idea of embodiment by unveiling inner execution subtleties to a class' subclasses.

3. Does inheritance restrict code reuse?

At times, inheritance could bring about various levels of inflexibility in the construction of the code, making it less conceivable to reuse code as changes to one class would need to be made to numerous others. This is because of the solid coupling between a class's way of behaving and execution when inheritance is utilized.

Leave a Reply

Your email address will not be published. Required fields are marked *