Inheritance

Python SELF EN
Level 15 , Lesson 6
Available

6.1 Inheritance is Easy

Inheritance is a fundamental concept of Object-Oriented Programming (OOP) that allows one class (called a child or subclass) to inherit fields and methods from another class (called a parent or superclass).

This approach enables creating more generic classes and reusing code, thus enhancing code organization and maintainability.

Why do you need inheritance?

Suppose you need to write some code and decide to do it in the form of a class. Then you find out there is already a class in your project that does almost everything you need. You could simply copy this class’s code into yours and enjoy.

Or you could "sort of copy". You could declare that class as the parent of your class, and then Python will add the behavior of the parent class to your class.

Imagine you are nature and want to create a Dog. What would be faster—creating a dog from a bacterium over a billion years or domesticating a wolf in 200 thousand years?

Basic inheritance example

Let's say you have a parent class Animal with a field name:


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

We want to create 2 child classes for it—Dog and Cat, and also add a method speak to both:


class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

In the example above, the child classes Dog and Cat inherit from Animal and add the speak method.

The Dog class:

  • Inherits the name attribute from Animal.
  • Adds a speak method that returns a string specific to dogs.

The Cat class:

  • Inherits the name attribute from Animal.
  • Adds a speak method that returns a string specific to cats.

The final version of the code looks like this:


class Animal:
    def __init__(self, name):
        self.name = name
         
class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"
        
class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"
         
dog = Dog("Buddy")
cat = Cat("Whiskers")
        
print(dog.speak())  # Output: Buddy says Woof!
print(cat.speak())  # Output: Whiskers says Meow!

In this example, Animal is the parent class, and Dog and Cat are child classes. The child classes inherit the name attribute and __init__ method from the parent class Animal, but add speak methods.

6.2 Under the Hood of Inheritance

If you add a parent to your class, it’s very much like you copied the parent class’s code into your class.


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

class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)  # Call to the parent class constructor
        
    def speak(self):
        return f"{self.name} says Woof!"
        
class Cat(Animal):
    def __init__(self, name):
        super().__init__(name)  # Call to the parent class constructor
        
    def speak(self):
        return f"{self.name} says Meow!"
        

dog = Dog("Buddy")
cat = Cat("Whiskers")
        
print(dog.speak())  # Output: Buddy says Woof!
print(cat.speak())  # Output: Whiskers says Meow!

This isn’t an entirely accurate description, but if you’ve never dealt with the concept of inheritance, you can think of it this way for now. We’ll add more details later.

6.3 Inheritance Hierarchy

Very often, when designing a complex model of a large group of classes, you might encounter an entire hierarchy of inheritance.

For example, you have a class Animal—this is the base class for all animals:


class Animal:
    def __init__(self, name):
        self.name = name
        
    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

We even added a speak method, but since a generic animal doesn’t speak, this method simply throws a NotImplementedError—this is standard practice.

Then we add intermediate classes that correspond to categories of animals: Mammal for mammals and Bird for birds.


class Mammal(Animal):
    def __init__(self, name, fur_color):
        super().__init__(name)  # Call to the parent class constructor
        self.fur_color = fur_color
         

class Bird(Animal):
    def __init__(self, name, wing_span):
        super().__init__(name)  # Call to the parent class constructor
        self.wing_span = wing_span
        
    def fly(self):
        return f"{self.name} is flying with a wingspan of {self.wing_span} meters."

And finally, only at the final stage do specific species classes appear:


class Dog(Mammal):
    def speak(self):
        return f"{self.name} says Woof!"
         
        
class Cat(Mammal):
    def speak(self):
        return f"{self.name} says Meow!"
        
class Parrot(Bird):
    def speak(self):
        return f"{self.name} says Squawk!"

This is usually the code you work with:


animals = [Dog("Buddy", "brown"), Cat("Whiskers", "white"), Parrot("Polly", 0.5)]
            
for animal in animals:
    print(animal.speak())

print(f"{dog.name} has {dog.fur_color} fur.")  # Output: Buddy has brown fur.
print(f"{cat.name} has {cat.fur_color} fur.")  # Output: Whiskers has white fur.
print(parrot.fly())  # Output: Polly is flying with a wingspan of 0.5 meters.

Although technically there are no restrictions on creating hierarchies with dozens of ancestors, it’s important to remember that, unless necessary, simplicity is best. Simplicity is power.

2
Task
Python SELF EN, level 15, lesson 6
Locked
Vehicles.
Vehicles.
2
Task
Python SELF EN, level 15, lesson 6
Locked
Shapes.
Shapes.
1
Опрос
Classes and OOP,  15 уровень,  6 лекция
недоступен
Classes and OOP
Classes and OOP
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION