sec04 - Mastering Inheritance in Python: Parent and Child Classes, super(), and Method Overriding
スポンサーリンク

Why Inheritance Is Necessary

As you continue writing programs with classes, you'll often find yourself wanting to create “similar but slightly different” classes.

For example, suppose you have a base class called Car and want to create ElectricCar and GasCar from it. Common behaviors for all cars (such as driving and braking) should be defined in the parent class. Meanwhile, features unique to each type of car (charging for electric cars, refueling for gas cars) should be written only in their child classes. This approach avoids redundant code and improves maintainability and scalability.

This concept of grouping shared functionality while organizing specific differences is called inheritance.

Basic Syntax and Structure of Inheritance in Python

To define inheritance, write the parent class name inside parentheses after the child class name.

The child class inherits and can access the parent class’s class variables, instance variables, and methods.

# Parent class
class Parent:
    parent_class_var = 'parent_class_var'

    def parent_method(self):
        print('parent_method')
        self.parent_instance_var = 'parent_instance_var'

# Child class (inherits from Parent)
class Child(Parent):
    # Even without defining anything, the child class can access the parent’s variables and methods
    pass

Let’s create an instance of the child class and check how methods, class variables, and instance variables behave.

child_instance = Child()
child_instance.parent_method()

print(Child.parent_class_var)
print(child_instance.parent_class_var)
print(child_instance.parent_instance_var)

Output:

parent_method
parent_class_var
parent_class_var
parent_instance_var

You can see that the methods and variables defined in the parent class are properly accessible from the child class.

スポンサーリンク

Exploring Inheritance Using the Car Class

Let’s start by creating a base class Car as the “generic car,” and then derive specialized versions for electric and gas cars.

# Parent class (defines common functionality)
class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def drive(self):
        print(f'{self.brand} {self.model} is now driving.')

    def brake(self):
        print(f'{self.brand} {self.model} is braking.')

# Child class ① Electric car
class ElectricCar(Car):
    # Electric cars can be charged
    def charge(self):
        print(f'Charging {self.brand} {self.model}.')

# Child class ② Gas car
class GasCar(Car):
    # Gas cars can be refueled
    def refuel(self):
        print(f'Refueling {self.brand} {self.model}.')

# Create instances of each child class
my_tesla = ElectricCar('Tesla', 'Model 3')
my_toyota = GasCar('Toyota', 'Corolla')

# Call methods
my_tesla.drive()   # Inherited from parent class
my_tesla.charge()  # Defined in child class

my_toyota.brake()   # Inherited from parent class
my_toyota.refuel()  # Defined in child class
Tesla Model 3 is now driving.
Charging Tesla Model 3.
Toyota Corolla is braking.
Refueling Toyota Corolla.

As shown, both ElectricCar and GasCar have their own unique behaviors while sharing the common drive() and brake() methods inherited from the parent class Car. This means you can reuse shared logic and only add the differences for each subclass.

What Is Overriding? How to Redefine a Parent Method in a Child Class

When a child class defines a method with the same name as one in its parent, it overrides the parent method.

For example, since electric cars have no engine sound, let’s override the drive() method in the ElectricCar class to reflect that behavior.

class ElectricCar(Car):
    def drive(self):
        print(f'{self.brand} {self.model} drives quietly (electric motor).')

To call the method, use the same syntax as before: instance.method().

my_tesla = ElectricCar('Tesla', 'Model 3')
my_tesla.drive()   # Executes the overridden method

Example output:

Tesla Model 3 drives quietly (electric motor).

When a method is overridden, the parent’s implementation is ignored, and only the child’s version is executed.

Using super(): Calling a Parent Method

Sometimes, you may want to keep the parent’s behavior while also adding new behavior in the child class. In that case, use super() to call the parent’s method in the format super().method().

Let’s add super().drive() inside the overridden drive() method.

class ElectricCar(Car):
    def drive(self):
        super().drive()  # Call the parent class’s drive() method
        print(f"{self.brand} {self.model} accelerates silently.")
my_tesla = ElectricCar('Tesla', 'Model 3')
my_tesla.drive()   # Executes the overridden method

Example output:

Tesla Model 3 is now driving.
Tesla Model 3 accelerates silently.

Using super() allows you to preserve the parent’s logic while adding subclass-specific actions.

When using super() in the __init__() method, the same rule applies: call it with super().__init__().

Final Example and Summary of Python Inheritance

# Parent class (defines shared functionality)
class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def drive(self):
        print(f'{self.brand} {self.model} is now driving.')

    def brake(self):
        print(f'{self.brand} {self.model} is braking.')

# Child class ① Electric car
class ElectricCar(Car):
    # Override the drive method
    def drive(self):
        super().drive()  # Call parent’s drive()
        print(f"{self.brand} {self.model} accelerates silently.")

    # Electric cars can be charged
    def charge(self):
        print(f"Charging {self.brand} {self.model}.")

# Child class ② Gas car
class GasCar(Car):
    # Gas cars can be refueled
    def refuel(self):
        print(f'Refueling {self.brand} {self.model}.')

# Tesla
my_tesla = ElectricCar('Tesla', 'Model 3')
my_tesla.drive()   # Overridden parent method
my_tesla.brake()   # Inherited
my_tesla.charge()  # Child-specific

# Toyota
my_toyota = GasCar('Toyota', 'Corolla')
my_toyota.drive()   # Inherited
my_toyota.brake()   # Inherited
my_toyota.refuel()  # Child-specific

Output:

Tesla Model 3 is now driving.
Tesla Model 3 accelerates silently.
Tesla Model 3 is braking.
Charging Tesla Model 3.
Toyota Corolla is now driving.
Toyota Corolla is braking.
Refueling Toyota Corolla.

By combining shared behavior in the parent class with unique logic in child classes, you can design code that’s both reusable and easy to understand.

スポンサーリンク