PYTHON

Python Inheritance: Syntax, Usage, and Examples

In Python, inheritance is a powerful feature of object-oriented programming that lets you create a new class based on an existing one. With Python inheritance, you can reuse code and extend the behavior of built-in or user-defined classes.

In many programming languages, inheritance is one of the main tools for building larger systems without rewriting the same logic again and again.


How to Use Python Inheritance

Python inheritance syntax involves defining a new class that takes an existing class as a parameter.

In other words, you create a base class, and then another class that class inherits from it.

class Parent:
    # Parent class code

class Child(Parent):
    # Child class inherits from Parent

The child class automatically gets access to the parent’s attributes and methods, unless overridden.

Here’s a simple example:

class Animal:
    def speak(self):
        print("Some sound")

class Dog(Animal):
    def bark(self):
        print("Woof!")

In this example, Dog inherits the speak() method from Animal and adds its own method bark().

The Dog class is also a derived class, which is another common term for a child class.


When to Use Inheritance in Python

Inheritance in Python is most useful when you want to:

1. Reuse Code Across Related Classes

Instead of copying and pasting code into multiple classes, inheritance lets you keep shared functionality in one place. You can define a base class for common behavior and create subclasses for specialized behavior.

This idea is often described as code reuse, and it’s one of the biggest reasons inheritance exists in OOP.

For example, if you're building a game with characters that share movement logic, but each has different attack methods, inheritance saves time.

2. Add or Modify Behavior Without Changing the Original Class

Sometimes, you want to add new functionality or tweak existing logic without touching the original class. Inheritance lets you override or extend methods without breaking existing functionality.

This is helpful when working with third-party libraries or maintaining backward compatibility in your own code.

3. Represent Real-World Hierarchies

Inheritance fits naturally with real-world relationships. For instance, if you have a Vehicle class, it makes sense to derive Car, Truck, and Motorcycle classes from it, since they all share core characteristics but differ in specific ways.

This also supports code readability because your classes can mirror how people already think about categories and relationships.


Examples of Python Inheritance

Here are some beginner-friendly examples to help you understand how inheritance works in real programs.

1. Inheriting Methods from a Parent Class

class Vehicle:
    def start(self):
        print("Engine started")

class Car(Vehicle):
    pass

my_car = Car()
my_car.start()  # Output: Engine started

Even though the Car class doesn't define the start() method, it inherits it from Vehicle.

2. Overriding Methods in a Subclass

class Animal:
    def speak(self):
        print("Animal makes a sound")

class Cat(Animal):
    def speak(self):
        print("Meow")

kitty = Cat()
kitty.speak()  # Output: Meow

Here, the Cat class overrides the speak() method to change the behavior defined in Animal.

3. Using the super() Function

The super() function allows you to call methods from the parent class, which is useful when you want to extend rather than replace the behavior.

The class you call super() from can be seen as a superclass, meaning the class your current class inherits from.

class Person:
    def greet(self):
        print("Hello!")

class Student(Person):
    def greet(self):
        super().greet()
        print("I’m a student.")

alex = Student()
alex.greet()
# Output:
# Hello!
# I’m a student.

Using super() helps you avoid duplicating code while still customizing behavior.


Learn More About Inheritance in Python

Python inheritance has some advanced features worth exploring as you grow in your programming journey.

Python Class Inheritance with __init__ Methods

When you define an __init__ method in a child class, the parent’s __init__ doesn’t automatically run. You need to explicitly call it using super().

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # Call parent constructor
        self.breed = breed

buddy = Dog("Buddy", "Golden Retriever")
print(buddy.name)   # Output: Buddy
print(buddy.breed)  # Output: Golden Retriever

This allows the child class to customize initialization while still leveraging the parent’s setup.

Python Multiple Inheritance

Python supports multiple inheritance, which means a class can inherit from more than one parent.

class Flyer:
    def fly(self):
        print("Flying")

class Swimmer:
    def swim(self):
        print("Swimming")

class Duck(Flyer, Swimmer):
    pass

donald = Duck()
donald.fly()   # Output: Flying
donald.swim()  # Output: Swimming

While powerful, Python multiple inheritance can get tricky if parent classes have overlapping methods. Python uses the method resolution order (MRO) to decide which method to call.

To inspect the method resolution order, use:

print(Duck.__mro__)

If you’re dealing with multiple parent classes that share method names, be mindful of the order in which you inherit them.

Python Multi Inheritance and the Diamond Problem

Consider this case, often called the “diamond problem”:

class A:
    def greet(self):
        print("Hello from A")

class B(A):
    def greet(self):
        print("Hello from B")

class C(A):
    def greet(self):
        print("Hello from C")

class D(B, C):
    pass

obj = D()
obj.greet()

This outputs:

Hello from B

Why? Because Python follows the MRO, which goes depth-first and left to right (D → B → C → A). So B’s version of greet() is used.

Understanding the MRO helps avoid surprises when using multi inheritance Python supports.

This setup is one of the types of inheritance you’ll see discussed when people compare inheritance models across languages.

Inherit in Python to Build Abstract Base Classes

Sometimes, you want to create a class that shouldn't be instantiated but should only serve as a blueprint. That’s where abstract base classes come in.

You can do this with the abc module:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

Now, trying to create an instance of Shape directly will raise an error, ensuring that only subclasses with a proper area() method can be used.

Private and Protected Members in Inheritance

Python doesn’t have strict access modifiers like some other languages, but naming conventions help communicate intent.

  • Prefixing a variable with a single underscore (_var) signals it’s intended for internal use.
  • Double underscores (__var) make the attribute name-mangled, which restricts access in subclasses.
class Parent:
    def __init__(self):
        self._internal = "should not be accessed directly"
        self.__private = "name-mangled"

class Child(Parent):
    def reveal(self):
        print(self._internal)
        # print(self.__private)  # This will raise an AttributeError

This allows you to define a more controlled inheritance structure, even in Python.

Inheritance and isinstance() Checks

Use the isinstance() function to check if an object belongs to a certain class or inherits from it.

class Animal:
    pass

class Dog(Animal):
    pass

buddy = Dog()
print(isinstance(buddy, Dog))    # True
print(isinstance(buddy, Animal)) # True

This is especially useful for polymorphic behavior—writing code that can accept objects from different subclasses of a common parent.

That style of code is called polymorphism, and inheritance makes it much easier to pull off.

Composition as an Alternative to Inheritance

While inheritance is useful, it's not always the best solution. Composition (using other objects inside a class) is often better when you don’t need a strict parent-child relationship.

class Engine:
    def start(self):
        print("Engine started")

class Car:
    def __init__(self):
        self.engine = Engine()

    def drive(self):
        self.engine.start()
        print("Driving")

Here, Car uses Engine, but doesn’t inherit from it. This gives more flexibility and avoids some of the complications of deep inheritance chains.


Types of Inheritance in Python

People often talk about inheritance patterns as “types,” especially when learning the concept. Here are a few you’ll hear about:

  • single inheritance: one child class inherits from one parent class
  • multilevel inheritance: a class inherits from a class that already inherits from another class
  • hierarchical inheritance: multiple child classes inherit from the same parent class
  • hybrid inheritance: a combination of more than one inheritance pattern

These patterns can help you describe your design choices when reading docs, talking to teammates, or explaining why your classes are structured a certain way.


A Practical Example with Related Roles

Here’s a small scenario you might use in real apps:

classEmployee:
def__init__(self, name):
self.name = name

defget_title(self):
return"Employee"

classManager(Employee):
defget_title(self):
return"Manager"

In this example, Employee is the base class, and Manager is the derived class. You could describe Employee as the class employee, and "Employee" is the human-friendly label returned by get_title(). The label Employee is also the class name for that base type.

When you create Manager("Sam"), you create a class object, meaning an instance of a class that carries data (name) and behavior (get_title()).


Using kwargs in Inherited Classes

When working with bigger class hierarchies, you’ll often see **kwargs, which is a way to accept and forward extra named arguments.

classPerson:
def__init__(self, **kwargs):
self.name = kwargs.get("name","Unknown")

classStudent(Person):
def__init__(self, **kwargs):
super().__init__(**kwargs)
self.major = kwargs.get("major","Undeclared")

This pattern becomes handy when your subclasses grow over time and you don’t want to keep rewriting the parameter list.


Summary

Python inheritance gives you a way to write cleaner, reusable code and model relationships between objects. You can build subclasses that reuse, customize, or extend functionality from a base class. With support for multiple inheritance, method overriding, and the super() function, Python offers flexible tools for building class hierarchies. Just keep an eye on complexity—especially when using Python multiple inheritance—and consider composition as a simpler alternative when appropriate.