How to inherit a class in Python

Learn how to inherit a class in Python. This guide covers methods, tips, real-world applications, and common errors to help you master it.

How to inherit a class in Python
Published on: 
Fri
Feb 20, 2026
Updated on: 
Mon
Apr 6, 2026
The Replit Team

Class inheritance is a core concept in Python's object-oriented programming. It allows you to build new classes that reuse and extend functionality from existing ones for cleaner, more organized code.

Here, you'll explore essential techniques and practical tips for class inheritance. You'll also find real-world applications and debugging advice to help you confidently implement this powerful feature.

Basic inheritance syntax

class Parent:
def greet(self):
return "Hello from Parent"

class Child(Parent):
pass

child = Child()
print(child.greet())--OUTPUT--Hello from Parent

The core of inheritance lies in the declaration class Child(Parent):. Placing the parent class inside the parentheses grants the child class access to its methods and attributes. This is why an instance of Child can successfully call the greet() method, even though it's defined in the Parent class. Understanding the fundamentals of creating a class in Python is essential before exploring inheritance.

  • The pass keyword simply means the Child class doesn't add any new functionality—it's a placeholder.
  • This structure allows you to reuse code effectively, creating a new class that inherits a complete set of behaviors without any duplication.

Core inheritance concepts

Building on this foundation, you can manage more complex class relationships by overriding methods, accessing parent functionality with super(), and inheriting from multiple parents.

Accessing parent class methods with super()

class Animal:
def make_sound(self):
return "Some generic sound"

class Dog(Animal):
def make_sound(self):
parent_sound = super().make_sound()
return f"{parent_sound} and a bark"

dog = Dog()
print(dog.make_sound())--OUTPUT--Some generic sound and a bark

The super() function is your tool for accessing methods from a parent class, even when you've overridden them. In this example, the Dog class provides its own make_sound() method, but it doesn't want to lose the original Animal functionality entirely. For more detailed techniques on using super() in Python, explore advanced patterns and best practices.

  • By calling super().make_sound(), you execute the parent's method first.
  • This allows you to build upon the parent's logic—in this case, adding a "bark" to the "generic sound"—rather than replacing it completely. It's a powerful way to extend functionality.

Method overriding in child classes

class Vehicle:
def max_speed(self):
return "Generic vehicle speed"

class Car(Vehicle):
def max_speed(self):
return "120 mph"

car = Car()
print(car.max_speed())--OUTPUT--120 mph

Method overriding occurs when a child class provides a specific implementation for a method that's already defined in its parent. Here, the Car class defines its own max_speed() method, which takes precedence over the one in the Vehicle class.

  • This allows you to create specialized behavior. When you call the method on a Car object, Python executes the child's version, effectively replacing the parent's more generic logic.

Multiple inheritance

class Flyable:
def fly(self):
return "I can fly"

class Swimmable:
def swim(self):
return "I can swim"

class Duck(Flyable, Swimmable):
pass

duck = Duck()
print(duck.fly())
print(duck.swim())--OUTPUT--I can fly
I can swim

Python isn't limited to single-parent inheritance; you can combine behaviors from multiple classes. By declaring class Duck(Flyable, Swimmable):, the Duck class inherits methods from both parents, allowing you to create composite classes that blend distinct functionalities.

  • An instance of Duck can call both fly() and swim(), even though its own class definition is empty.
  • This approach is great for mixing and matching behaviors—like different types of movement—without duplicating code.

Advanced inheritance techniques

Building on concepts like multiple inheritance, you can create more robust designs using abstract base classes, mixins, and by understanding Python's Method Resolution Order.

Using abstract base classes

from abc import ABC, abstractmethod

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

class Square(Shape):
def __init__(self, side):
self.side = side

def area(self):
return self.side ** 2

square = Square(5)
print(f"Area: {square.area()}")--OUTPUT--Area: 25

An abstract base class (ABC) acts as a blueprint, defining methods that child classes must implement. By inheriting from ABC and using the @abstractmethod decorator, the Shape class establishes a contract. It guarantees that any subclass will have an area() method.

  • This structure enforces a consistent interface. You can't create an instance of Shape directly.
  • Any child class, like Square, is required to provide its own area() implementation. If it doesn't, Python raises an error, ensuring all shapes are guaranteed to have this functionality.

Implementing mixin classes

class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)

class User(JSONSerializableMixin):
def __init__(self, name, age):
self.name = name
self.age = age

user = User("Alice", 30)
print(user.to_json())--OUTPUT--{"name": "Alice", "age": 30}

A mixin is a class designed to add a specific feature to other classes without being a parent in the traditional sense. Think of it as plugging in a piece of functionality. Here, the JSONSerializableMixin provides a reusable to_json() method that any class can adopt, leveraging techniques for converting dictionaries to JSON.

  • By inheriting from the mixin, the User class instantly gains the ability to serialize itself to JSON.
  • This approach keeps the User class focused on its main job—managing user data—while the mixin handles the separate concern of JSON conversion. It's a clean way to share functionality across unrelated classes.

Understanding Method Resolution Order (MRO)

class A:
def who_am_i(self):
return "Class A"

class B(A):
def who_am_i(self):
return "Class B"

class C(A):
def who_am_i(self):
return "Class C"

class D(B, C):
pass

d = D()
print(d.who_am_i())
print(D.__mro__)--OUTPUT--Class B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

Method Resolution Order (MRO) is the rule Python follows to determine which method to run in complex inheritance chains. When a class like D inherits from multiple parents—in this case, B and C—Python needs a clear path to resolve method calls, avoiding ambiguity.

  • The __mro__ attribute reveals this exact path. For class D, the order is D, B, C, and finally A.
  • When you call d.who_am_i(), Python checks D first, finds nothing, then moves to B. Since B has the method, its version is executed and the search ends.

Move faster with Replit

Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. Describe what you want to build, and Agent 4 handles everything—from writing the code and connecting databases to deploying it live.

Instead of piecing together techniques, you can describe the app you actually want to build and let the Agent take it from idea to working product:

  • A unit converter where different classes like LengthConverter and WeightConverter inherit from a base Converter, each providing a specialized convert() method.
  • A UI component library where a JSONMixin adds export functionality to different widget classes like Button and Slider.
  • A notification system where EmailNotifier and SMSNotifier extend a base class, using super() to add delivery logic to a shared message formatting method.

Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

Even with a solid grasp of inheritance, you'll run into a few common pitfalls that can be tricky to debug and may require code repair techniques.

Debugging misspelled method names when overriding

A simple typo is one of the most frequent sources of bugs when overriding methods. If you intend to override a parent method but misspell its name in the child class, Python won't raise an error. Instead, it treats your misspelled version as a brand new method, and any calls to the original method name will silently execute the parent's version, leading to behavior you didn't expect.

Resolving method conflicts in multiple inheritance

When a class inherits from multiple parents that have methods with the same name, it can be unclear which one gets called. Python resolves this using the Method Resolution Order (MRO). If your code isn't behaving as expected, the conflict likely lies here. You can inspect the `__mro__` attribute of your class to see the exact lookup order Python uses and identify which parent's method is taking precedence.

Troubleshooting incomplete abstract class implementations

Abstract base classes enforce a contract that requires subclasses to implement specific methods. If you forget to implement any method marked with the @abstractmethod decorator, Python will stop you from creating an instance of that subclass by raising a TypeError. The error message is usually quite helpful—it will explicitly tell you which abstract methods are still undefined, making the fix straightforward.

Debugging misspelled method names when overriding

It's easy to misspell a method name when you're overriding it, which creates a subtle bug. Python won't throw an error; it just runs the parent's method instead of yours. The code below shows how this silent failure can happen.

class Parent:
def process_data(self, data):
return f"Processing {data}"

class Child(Parent):
# Misspelled method name - not actually overriding
def proccess_data(self, data):
return f"Child processing {data}"

child = Child()
print(child.process_data("sample")) # Uses Parent's method

The Child class defines proccess_data, but the call is for process_data. Since the names don't match, Python executes the Parent's method instead, silently ignoring the intended override. The following code demonstrates the correct implementation.

class Parent:
def process_data(self, data):
return f"Processing {data}"

class Child(Parent):
# Correct method name for overriding
def process_data(self, data):
return f"Child processing {data}"

child = Child()
print(child.process_data("sample")) # Uses Child's method

By correcting the typo to process_data, the Child class now successfully overrides the parent's method. When you call child.process_data("sample"), Python finds the matching method in the child and executes it, giving you the specialized behavior you intended. This kind of silent failure is common, so always double-check your method names when overriding—especially if the parent's logic runs unexpectedly.

Resolving method conflicts in multiple inheritance

When you combine classes that share method names, like execute(), Python doesn't get confused—it simply follows a predictable order to decide which one to run. This can lead to silent bugs where the wrong method is called, as you'll see below.

class Service1:
def execute(self):
return "Service 1 execution"

class Service2:
def execute(self):
return "Service 2 execution"

class CombinedService(Service1, Service2):
pass

service = CombinedService()
print(service.execute()) # Only calls Service1's method

Because CombinedService inherits from Service1 first, Python calls its execute() method and ignores the version in Service2. This can hide functionality unexpectedly. The following example demonstrates how to resolve this conflict.

class Service1:
def execute(self):
return "Service 1 execution"

class Service2:
def execute(self):
return "Service 2 execution"

class CombinedService(Service1, Service2):
def execute(self):
s1_result = Service1.execute(self)
s2_result = Service2.execute(self)
return f"{s1_result} and {s2_result}"

service = CombinedService()
print(service.execute())

The solution is to override the conflicting method in the child class. By defining a new execute() method in CombinedService, you can explicitly call each parent's version using Service1.execute(self) and Service2.execute(self). This lets you combine their results instead of letting one silently override the other. Keep an eye on this when using mixins or inheriting from multiple third-party libraries, where method name clashes are more likely.

Troubleshooting incomplete abstract class implementations

Abstract base classes enforce a contract that requires child classes to implement specific methods. If you forget even one, Python won't let you create an object from your class, raising a TypeError because the contract is broken. The code below shows what happens when a class inherits from a Database ABC but fails to implement the required query() method.

from abc import ABC, abstractmethod

class Database(ABC):
@abstractmethod
def connect(self):
pass

@abstractmethod
def query(self, sql):
pass

class SQLiteDB(Database):
def connect(self):
return "Connected to SQLite"

# Missing query method implementation

db = SQLiteDB() # Will raise TypeError

Because the SQLiteDB class doesn't implement the required query() method, it breaks the contract set by the abstract class. This causes a TypeError when you try to create an instance. The following example provides the necessary implementation.

from abc import ABC, abstractmethod

class Database(ABC):
@abstractmethod
def connect(self):
pass

@abstractmethod
def query(self, sql):
pass

class SQLiteDB(Database):
def connect(self):
return "Connected to SQLite"

def query(self, sql):
return f"Executing: {sql}"

db = SQLiteDB()
print(db.connect())
print(db.query("SELECT * FROM users"))

By implementing the missing query() method, the SQLiteDB class now fulfills the contract required by its abstract parent. This fixes the TypeError and makes the class instantiable. You'll run into this when working with frameworks or plugin architectures that demand specific methods be present, ensuring all components behave predictably. This approach guarantees a consistent API across different implementations.

Real-world applications

Beyond the syntax and debugging, inheritance is a powerful tool for structuring complex applications like data processors and game frameworks, especially when combined with vibe coding approaches.

Building a data processor hierarchy with inheritance

Inheritance lets you build a flexible hierarchy for handling different data formats by creating a base DataProcessor for shared logic and letting subclasses override specific methods like parse().

class DataProcessor:
def process(self, data):
cleaned_data = self.clean(data)
return self.parse(cleaned_data)

def clean(self, data):
return data.strip()

def parse(self, data):
return data # Default implementation

class CSVProcessor(DataProcessor):
def parse(self, data):
lines = data.split('\n')
return [line.split(',') for line in lines]

# Example usage
csv_processor = CSVProcessor()
sample_data = "name,age\nAlice,30\nBob,25"
result = csv_processor.process(sample_data)
print(result)

The DataProcessor class establishes a two-step workflow in its process() method, which calls clean() and then parse(). The CSVProcessor inherits this entire workflow but provides its own specialized version of the parse() method to handle comma-separated data. For more robust CSV handling, explore comprehensive techniques for reading CSV files in Python.

  • When you call process() on a CSVProcessor object, it executes the shared clean() logic from the parent.
  • It then runs its own custom parse() implementation, effectively reusing the overall processing steps while tailoring a specific part for a different data type.

Creating a simple game framework with inheritance

You can structure a simple game by creating a base GameObject with update() and draw() methods, allowing subclasses like Player to inherit this framework and add their own unique features.

class GameObject:
def __init__(self, x=0, y=0):
self.x = x
self.y = y

def update(self):
pass

def draw(self):
print(f"Drawing object at ({self.x}, {self.y})")

class Player(GameObject):
def __init__(self, x=0, y=0, health=100):
super().__init__(x, y)
self.health = health

def update(self):
self.health = min(100, self.health + 1) # Regenerate health

def draw(self):
print(f"Drawing player at ({self.x}, {self.y}) with health: {self.health}")

player = Player(10, 20, 80)
for _ in range(2):
player.update()
player.draw()

The Player class inherits core position tracking from GameObject but gives it a unique purpose. It calls super().__init__() to handle the initial x and y setup, then adds its own health attribute for game-specific logic. Both the update() and draw() methods are overridden to create specialized behavior.

  • The Player's update() method implements health regeneration, replacing the empty method from the parent.
  • Its custom draw() method provides more detailed output by including the current health, making it more useful than the generic parent version.

Get started with Replit

Turn your knowledge into a real tool. Describe what you want to build to Replit Agent, like “a unit converter with a base class and subclasses for length and weight” or “a data parser for CSV and JSON files.”

Replit Agent will write the code, test for errors, and deploy your application. Start building with Replit.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.