Class inheritance

Class inheritance allows us to create a new class (called a child class or subclass) based on an existing class (called a parent class or superclass). The child class inherits the attributes and methods of the parent class, allowing us to reuse and extend the functionality of the parent class.

Let's take an example of a Vehicle class and a Car class to understand how class inheritance works:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def start_engine(self):
        print("Engine started!")

    def stop_engine(self):
        print("Engine stopped!")


class Car(Vehicle):
    def drive(self):
        print("Car is being driven!")

In this code, we define a Vehicle class with attributes make, model, and year, and methods start_engine and stop_engine. We also define a Car class that inherits from the Vehicle class using the syntax class Car(Vehicle):.

The Car class inherits the attributes and methods of the Vehicle class. It also defines its own method called drive, which is specific to cars.

To create an object of the Car class and access its attributes and methods, we can do the following:

my_car = Car("Toyota", "Camry", 2022)
print(my_car.make)  # Output: Toyota
print(my_car.model)  # Output: Camry
print(my_car.year)  # Output: 2022

my_car.start_engine()  # Output: Engine started!
my_car.drive()  # Output: Car is being driven!
my_car.stop_engine()  # Output: Engine stopped!

In this code, we create an object of the Car class called my_car. We can access the attributes make, model, and year inherited from the Vehicle class, as well as call the methods start_engine, drive, and stop_engine inherited from the Vehicle class.

Class inheritance allows us to create specialized classes that inherit and extend the functionality of a more general class. It promotes code reuse, modularity, and flexibility in your programs.

You can also override methods inherited from the parent class in the child class to provide custom behavior. This allows us to modify or extend the functionality of the inherited methods. Here's an example:

class Car(Vehicle):
    def drive(self):
        print("Car is being driven!")

    def stop_engine(self):
        print("Car engine stopped!")  # Override the stop_engine method

In this code, we override the stop_engine method in the Car class to provide a custom implementation specific to cars.