Python tutorials  /  Inheritance in Python
Chapter 14 · Python

Inheritance in Python

Inheritance is a mechanism in object-oriented programming that allows a class (called a child class or derived class) to inherit attributes and methods from another class (called a parent class or base class). Through inheritance, you can create a new class that reuses code from an existing class while extending or modifying its behavior. Inheritance establishes a hierarchical relationship between classes, enabling code reuse and creating logical classifications that mirror real-world hierarchies.

Imagine inheritance like family traits. A parent passes certain characteristics to their children. Children inherit basic traits from parents but can also have their own unique traits. In programming, a parent class (like 'Vehicle') defines common properties and behaviors. Child classes (like 'Car' and 'Motorcycle') inherit these basics from the parent but add their own specialized features. A car inherits the ability to 'start_engine' from Vehicle but adds car-specific methods like 'open_trunk'. This saves you from rewriting common code in every child class.

Consider an e-commerce system with different user types. A parent class 'User' defines common attributes (username, email, password) and methods (login, logout, update_profile). Then you create child classes: 'Customer' inherits from User and adds shopping-specific methods like add_to_cart() and place_order(). An 'Vendor' class also inherits from User but adds vendor-specific methods like upload_product() and view_sales(). Both Customer and Vendor avoid rewriting login logic - they inherit it from User. This structure reflects real-world relationships and makes adding new user types simple: just create a new child class inheriting from User.

Inheritance solves critical software development challenges: (1) Code Reusability - avoid writing duplicate code by reusing parent class code, (2) Logical Organization - structure classes hierarchically matching real-world relationships, (3) Maintainability - fix bugs or make changes in parent class, automatically benefiting all child classes, (4) Extensibility - easily extend existing classes with new functionality without modifying original code, (5) Polymorphism - treat objects of different child classes uniformly through parent class interface, (6) Reduced Redundancy - common operations live in one place, reducing errors, (7) Scalability - large systems become manageable when organized hierarchically.

  • Single Inheritance: A child class inherits from exactly one parent class. This is the simplest form of inheritance and creates a straightforward parent-child relationship.
  • Multiple Inheritance: A child class inherits from two or more parent classes. This allows combining features from multiple sources but can create complexity with the Method Resolution Order (MRO).
  • Multilevel Inheritance: A class inherits from a parent, which itself inherits from another parent, creating a chain. Example: GrandParent -> Parent -> Child.
  • Hierarchical Inheritance: Multiple child classes inherit from a single parent class. One parent serves as the base for several specialized child classes.
# Parent (Base) Class class ParentClass: def __init__(self, attribute): self.attribute = attribute def parent_method(self): return "Parent method" # Child Class inheriting from Parent class ChildClass(ParentClass): def __init__(self, attribute, child_attribute): # Call parent constructor super().__init__(attribute) self.child_attribute = child_attribute # Override parent method def parent_method(self): return "Child method" # Add child-specific method def child_method(self): return "Child-specific method" # Multiple Inheritance class MultiChild(ParentClass, AnotherParent): pass # Creating objects child_obj = ChildClass(value1, value2) child_obj.parent_method() # Calls overridden method child_obj.child_method() # Calls child-specific method # Check inheritance isinstance(child_obj, ParentClass) # Returns True issubclass(ChildClass, ParentClass) # Returns True
Imagine building a game with different character types: Warrior, Mage, and Archer. Without inheritance, you'd write redundant code in each class for common functionality like taking damage, gaining experience, and moving. With inheritance, you create a base Character class with these common features, then create Warrior, Mage, and Archer as child classes. Each child class adds its specialized abilities. If you later need to fix a bug in damage calculation, you fix it once in the Character class instead of three places. Adding new character types becomes simple - just create a new child class inheriting from Character.
Single Inheritance - Basic Parent and Child Classes
Creating a parent class and child class with inheritance and method overriding.
python
# Parent class class Animal: def __init__(self, name, age): self.name = name self.age = age def eat(self): return f"{self.name} is eating" def sleep(self): return f"{self.name} is sleeping" def make_sound(self): return f"{self.name} makes a sound" # Child class inheriting from Animal class Dog(Animal): def __init__(self, name, age, breed): super().__init__(name, age) self.breed = breed # Override parent method def make_sound(self): return f"{self.name} barks: Woof! Woof!" # Child-specific method def fetch(self): return f"{self.name} fetches the ball" # Create objects dog = Dog("Rex", 5, "Labrador") print(dog.eat()) print(dog.make_sound()) print(dog.fetch()) print(f"Breed: {dog.breed}")
Rex is eating Rex barks: Woof! Woof! Rex fetches the ball Breed: Labrador
Dog inherits eat() and sleep() methods from Animal. The make_sound() method is overridden in Dog to provide dog-specific behavior. super().__init__() calls the parent constructor to properly initialize inherited attributes. The Dog class adds its own breed attribute and fetch() method, demonstrating how inheritance enables code reuse while allowing customization.
Multilevel Inheritance - Chain of Classes
Creating a hierarchy where a child class becomes a parent to another class.
python
# Grandparent class class Vehicle: def __init__(self, brand, model): self.brand = brand self.model = model def start_engine(self): return f"{self.brand} {self.model} engine started" # Parent class inheriting from Vehicle class Car(Vehicle): def __init__(self, brand, model, doors): super().__init__(brand, model) self.doors = doors def open_trunk(self): return f"{self.brand} {self.model} trunk is open" # Child class inheriting from Car class ElectricCar(Car): def __init__(self, brand, model, doors, battery_capacity): super().__init__(brand, model, doors) self.battery_capacity = battery_capacity def charge_battery(self): return f"{self.brand} {self.model} charging battery ({self.battery_capacity}kWh)" # Create object and test methods electric_car = ElectricCar("Tesla", "Model 3", 4, 75) print(electric_car.start_engine()) # From Vehicle print(electric_car.open_trunk()) # From Car print(electric_car.charge_battery()) # From ElectricCar
Tesla Model 3 engine started Tesla Model 3 trunk is open Tesla Model 3 charging battery (75kWh)
ElectricCar inherits from Car, which inherits from Vehicle, creating a three-level hierarchy. ElectricCar has access to methods from all levels. Each constructor calls super().__init__() to properly initialize the chain. This demonstrates how multilevel inheritance creates logical hierarchies matching real-world relationships.
Method Overriding and super() Function
Overriding parent methods and using super() to access parent functionality.
python
class Employee: def __init__(self, name, salary): self.name = name self.salary = salary def calculate_bonus(self): return self.salary * 0.1 # 10% bonus def display_info(self): return f"Name: {self.name}, Salary: ${self.salary}" class Manager(Employee): def __init__(self, name, salary, team_size): super().__init__(name, salary) self.team_size = team_size # Override calculate_bonus with different logic def calculate_bonus(self): return self.salary * 0.2 # Managers get 20% bonus # Override and extend display_info def display_info(self): parent_info = super().display_info() return f"{parent_info}, Team Size: {self.team_size}" # Create and test objects employee = Employee("John", 50000) manager = Manager("Alice", 70000, 5) print(employee.display_info()) print(f"Employee bonus: ${employee.calculate_bonus()}") print() print(manager.display_info()) print(f"Manager bonus: ${manager.calculate_bonus()}")
Name: John, Salary: $50000 Employee bonus: $5000.0 Name: Alice, Salary: $70000, Team Size: 5 Manager bonus: $14000.0
Manager overrides calculate_bonus() with different logic (20% vs 10%). The display_info() method uses super() to call the parent implementation and extend it with manager-specific information. This shows method overriding for completely different behavior and using super() to extend parent functionality while avoiding code duplication.
Multiple Inheritance - Inheriting from Multiple Parents
Creating a class that inherits from multiple parent classes.
python
# First parent class class FlyingAbility: def fly(self): return "Flying through the sky" # Second parent class class SwimmingAbility: def swim(self): return "Swimming in the water" # Child class inheriting from both parents class Duck(FlyingAbility, SwimmingAbility): def __init__(self, name): self.name = name def quack(self): return f"{self.name} says: Quack!" def display_abilities(self): return f"{self.name} can: {self.fly()}, {self.swim()}, and {self.quack()}" # Create and test duck = Duck("Donald") print(duck.fly()) print(duck.swim()) print(duck.quack()) print(duck.display_abilities()) print(f"MRO: {Duck.__mro__}")
Flying through the sky Swimming in the water Donald says: Quack! Donald can: Flying through the sky, Swimming in the water, and Donald says: Quack! MRO: (<class 'Duck'>, <class 'FlyingAbility'>, <class 'SwimmingAbility'>, <class 'object'>)
Duck inherits from both FlyingAbility and SwimmingAbility, combining abilities from multiple parents. This demonstrates multiple inheritance allowing a class to inherit from multiple sources. The MRO (Method Resolution Order) shows the order Python searches for methods, determined by the parent order in the class definition.
Hierarchical Inheritance - Multiple Children from One Parent
Creating multiple child classes from a single parent class.
python
# Parent class class Shape: def __init__(self, color): self.color = color def describe(self): return f"This is a {self.color} shape" # Child class 1 class Circle(Shape): def __init__(self, color, radius): super().__init__(color) self.radius = radius def area(self): import math return math.pi * self.radius ** 2 # Child class 2 class Rectangle(Shape): def __init__(self, color, length, width): super().__init__(color) self.length = length self.width = width def area(self): return self.length * self.width # Child class 3 class Triangle(Shape): def __init__(self, color, base, height): super().__init__(color) self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # Create objects and demonstrate polymorphism shapes = [Circle("red", 5), Rectangle("blue", 4, 6), Triangle("green", 3, 4)] for shape in shapes: print(shape.describe()) print(f"Area: {shape.area():.2f}") print()
This is a red shape Area: 78.54 This is a blue shape Area: 24.00 This is a green shape Area: 6.00
Circle, Rectangle, and Triangle all inherit from Shape, demonstrating hierarchical inheritance. Each child class implements its own area() method differently. The loop shows polymorphism - treating different objects through a common parent interface. This structure is scalable: adding new shape types only requires creating new child classes inheriting from Shape.
  • Forgetting to Call super().__init__() in Child Constructor: If a child class has __init__, it completely overrides the parent's constructor. Forgetting to call super().__init__() means parent attributes won't be initialized. This causes AttributeError when parent methods try to access those attributes. Always call super().__init__() with appropriate arguments to properly initialize the parent class.
  • Incorrectly Using super() in Multiple Inheritance: Multiple inheritance with incorrect super() usage can skip some parent classes or cause unexpected behavior due to Method Resolution Order (MRO). Python determines method lookup order through MRO, not simple left-to-right. Understanding MRO is crucial for multiple inheritance. Check MRO using ClassName.__mro__ to debug issues.
  • Creating Overly Deep Inheritance Hierarchies: Having too many levels of inheritance makes code hard to understand and maintain. Deep hierarchies (GrandParent -> Parent -> Child -> GrandChild) make it difficult to track which method is being called. Keep hierarchies shallow (2-3 levels) and consider composition as an alternative when hierarchies get deep.
  • Not Respecting Liskov Substitution Principle: Child classes should be usable wherever parent classes are expected without breaking code. If a child class changes the expected behavior too drastically, it violates this principle. Ensure child classes truly are 'a kind of' parent class, not just sharing some methods by chance.
  • Abusing Multiple Inheritance: Multiple inheritance can quickly become confusing, especially with shared methods between parents. If you find multiple inheritance creates complexity, consider using composition instead. A class can have instances of other classes as attributes rather than inheriting from them.
  • Mixing Inheritance and Instance Variables Incorrectly: Child classes should properly handle both inherited and new instance variables. Accidentally shadowing parent variables or not initializing child variables in __init__ leads to subtle bugs. Always be explicit about which variables belong to which class.
  • Follow the Liskov Substitution Principle: Child classes should be substitutable for parent classes. If a function expects a Vehicle, passing a Car (child of Vehicle) should work correctly. This means child classes should maintain the contract established by parent classes, not change expected behavior unexpectedly.
  • Prefer Composition Over Inheritance When Appropriate: Not every relationship should be inheritance. If a class 'has-a' something rather than 'is-a' something, use composition. A Car 'is-a' Vehicle (inheritance), but a Car 'has-a' Engine (composition). Composition is often simpler and more flexible than inheritance.
  • Keep Inheritance Hierarchies Shallow: Limit inheritance depth to 2-3 levels. Deep hierarchies become hard to understand and maintain. If your hierarchy gets deeper, reconsider your design. Consider composition or flattening the hierarchy.
  • Use super() to Access Parent Methods: When extending parent functionality in child classes, use super() instead of directly calling parent methods by class name. This respects MRO and works correctly with multiple inheritance. Example: super().__init__() instead of ParentClass.__init__(self).
  • Document Inheritance Relationships: Include docstrings explaining the inheritance hierarchy and what each class adds or overrides. This helps developers understand the relationships and design intent. Make the 'is-a' relationship explicit in documentation.
  • Override Methods Intentionally: Only override methods when you have a clear reason - typically to specialize behavior for child classes. Don't override methods accidentally or out of habit. Each override should serve a purpose in the class's logic.
  • Use Abstract Base Classes for Interface Definition: When creating a parent class meant to define an interface (methods all children should implement), consider using Python's ABC module. This documents intent and can enforce implementation of required methods in child classes.
What is inheritance and why is it important in OOP?
Inheritance is a mechanism where a child class derives attributes and methods from a parent class. It's important because it promotes code reusability - common functionality lives in the parent class, reducing duplication. It enables logical organization matching real-world hierarchies and allows extending existing code without modifying it. Changes in parent class automatically benefit all child classes, improving maintainability. Inheritance also enables polymorphism, allowing different child classes to be treated uniformly through the parent interface.
Easy
What is the difference between single, multiple, and multilevel inheritance?
Single inheritance: a child class inherits from one parent class (Child -> Parent). Multiple inheritance: a child class inherits from two or more parents (Child inherits from Parent1 and Parent2). Multilevel inheritance: a chain of inheritance where a child becomes a parent to another child (GrandParent -> Parent -> Child). Single is simplest and most common. Multiple adds complexity with Method Resolution Order. Multilevel creates hierarchies but shouldn't be too deep.
Medium
What does the super() function do and why is it important?
super() returns a temporary object of the parent class, allowing access to parent methods and attributes. It's important because it allows child classes to extend parent functionality without duplicating code. Example: super().__init__() calls the parent constructor to properly initialize inherited attributes. Using super() respects Method Resolution Order in multiple inheritance and makes code more flexible - if parent class changes, child automatically uses the new version.
Medium
What is method overriding and how does it work?
Method overriding is when a child class defines a method with the same name as a parent class method. The child's version replaces the parent's version. When you call the method on a child object, Python uses the child's implementation. This allows child classes to specialize behavior for their specific needs. Example: Animal has make_sound() method. Dog overrides it to return 'Woof!' instead of generic sound. Overriding enables polymorphism.
Medium
Explain the Method Resolution Order (MRO) and how it affects multiple inheritance.
MRO determines the order Python searches for methods in a class hierarchy. With multiple inheritance, MRO prevents methods from being called multiple times and ensures each parent is visited only once. Python uses the C3 Linearization algorithm. You can view MRO using ClassName.__mro__ or help(ClassName). Understanding MRO is crucial for multiple inheritance - it explains which method gets called when multiple parents define the same method. Left-to-right and depth-first are general rules, but MRO can be complex.
Medium
What's the difference between 'is-a' (inheritance) and 'has-a' (composition) relationships?
'is-a' relationship indicates inheritance: Dog is-a Animal. 'has-a' relationship indicates composition: Car has-a Engine. Use inheritance when child class truly is a type of parent. Use composition when one object contains another as a component. Composition is often more flexible and avoids deeply nested hierarchies. Example: Car shouldn't inherit from Engine; instead, Car should have an Engine attribute. Choose composition to avoid inflexible inheritance structures.
Medium
What is the Liskov Substitution Principle and why does it matter for inheritance?
The Liskov Substitution Principle states that objects of child classes should be substitutable for objects of parent classes without breaking the program. If code expects a Vehicle, passing a Car should work correctly because Car is-a Vehicle. Violating this creates subtle bugs where child class behavior differs unexpectedly from parent. This principle ensures inheritance creates true hierarchical relationships, not just code sharing.
Hard
How would you handle a situation where you need functionality from multiple unrelated classes?
Multiple inheritance can work but creates complexity. Better alternatives include: (1) Composition - have objects of those classes as attributes and delegate to them, (2) Mixins - small classes providing specific functionality that multiple classes can inherit from, (3) Design patterns like Decorator - wrap objects to add functionality. Evaluate if you truly need all parent functionality or if composition would be simpler and more maintainable.
Hard
Create a parent class 'Transportation' with attributes for speed and distance. Create child classes 'Car' and 'Bicycle' that inherit from Transportation. Each child should calculate travel time differently and display their info.
class Transportation: def __init__(self, speed, distance): self.speed = speed self.distance = distance def travel_time(self): return self.distance / self.speed def display_info(self): return f"Speed: {self.speed} km/h, Distance: {self.distance} km" class Car(Transportation): def __init__(self, speed, distance, fuel_type): super().__init__(speed, distance) self.fuel_type = fuel_type def travel_time(self): base_time = super().travel_time() return base_time # Add 10% for traffic def display_info(self): return f"{super().display_info()}, Fuel: {self.fuel_type}" class Bicycle(Transportation): def __init__(self, speed, distance, bike_type): super().__init__(speed, distance) self.bike_type = bike_type def travel_time(self): return super().travel_time() # No modification def display_info(self): return f"{super().display_info()}, Type: {self.bike_type}" car = Car(100, 200, "Diesel") bike = Bicycle(20, 50, "Mountain") print(car.display_info()) print(f"Travel time: {car.travel_time():.2f} hours") print(bike.display_info()) print(f"Travel time: {bike.travel_time():.2f} hours")
Easy
Create a multilevel inheritance hierarchy: Person -> Student -> GraduateStudent. Add appropriate attributes and methods to each level, demonstrating how each level extends the previous one.
class Person: def __init__(self, name, age): self.name = name self.age = age def display_info(self): return f"Name: {self.name}, Age: {self.age}" class Student(Person): def __init__(self, name, age, student_id, gpa): super().__init__(name, age) self.student_id = student_id self.gpa = gpa def display_info(self): return f"{super().display_info()}, ID: {self.student_id}, GPA: {self.gpa}" class GraduateStudent(Student): def __init__(self, name, age, student_id, gpa, thesis_title, advisor): super().__init__(name, age, student_id, gpa) self.thesis_title = thesis_title self.advisor = advisor def display_info(self): return f"{super().display_info()}, Thesis: {self.thesis_title}, Advisor: {self.advisor}" grad_student = GraduateStudent("Alice", 24, "G001", 3.9, "AI in Medicine", "Dr. Smith") print(grad_student.display_info())
Medium
Create a practical example with multiple inheritance where a 'SwimmingPool' class inherits from both 'Building' and 'WaterFacility'. Show how super() and MRO work together.
class Building: def __init__(self, address, year_built): self.address = address self.year_built = year_built def describe(self): return f"Building at {self.address}, built in {self.year_built}" class WaterFacility: def __init__(self, water_capacity, water_type): self.water_capacity = water_capacity self.water_type = water_type def describe(self): return f"Water facility with {self.water_capacity}L of {self.water_type}" class SwimmingPool(Building, WaterFacility): def __init__(self, address, year_built, water_capacity, water_type, lane_count): Building.__init__(self, address, year_built) WaterFacility.__init__(self, water_capacity, water_type) self.lane_count = lane_count def describe(self): return f"{Building.describe(self)}, {WaterFacility.describe(self)}, Lanes: {self.lane_count}" pool = SwimmingPool("123 Main St", 2015, 10000, "Chlorinated", 8) print(pool.describe()) print(f"MRO: {SwimmingPool.__mro__}")
Medium

Inheritance is a cornerstone of Object-Oriented Programming that enables creating hierarchies of classes with increasing specialization. Key concepts include: (1) Parent and Child Classes - establishing 'is-a' relationships, (2) Method Overriding - child classes customizing parent behavior, (3) super() Function - accessing parent methods while respecting inheritance hierarchy, (4) Types of Inheritance - single, multiple, multilevel, hierarchical, each with different use cases, (5) Method Resolution Order (MRO) - determining method lookup order, crucial for multiple inheritance. Inheritance promotes code reuse, reduces redundancy, and allows building complex systems from simple base classes. However, it should be used thoughtfully - deep hierarchies become hard to maintain, and composition is sometimes better than inheritance. Understanding when to use inheritance versus composition, and keeping hierarchies reasonable, marks skilled OOP design. Mastering inheritance prepares you for polymorphism, where different classes respond differently to the same method call, enabling powerful abstraction.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.