PYTHON • OBJECT-ORIENTED PROGRAMMING

Inheritance & Method Overriding — Use inheritance and method overriding (intro)

Learn how classes can share and customize behavior by inheriting from parent classes and overriding their methods.

Historical Context & Motivation

As software projects grew larger through the 1960s and 1970s, programmers realized that copying and pasting the same code into dozens of files was a recipe for bugs and confusion. If you needed to change how a single feature worked, you might have to update it in twenty different places. Inheritance was invented as a way for one class to automatically receive the attributes and methods of another, so shared behavior could live in just one place. This single idea became one of the pillars of object-oriented programming (OOP).

1967
Simula Introduces Inheritance
Norwegian computer scientists Ole-Johan Dahl and Kristen Nygaard created Simula, the first language to support classes and inheritance. They wanted to model real-world objects such as ships and passengers in simulations.
1972
Smalltalk Refines OOP
Alan Kay and his team at Xerox PARC built Smalltalk, a fully object-oriented language where every value is an object. Smalltalk popularized the idea of sending messages to objects and overriding behavior in subclasses.
1991
Python Is Released
Guido van Rossum released Python 0.9.0, which already included classes with inheritance. Python's clean syntax made OOP accessible to beginners and professionals alike.
2008
Python 3 Modernizes Classes
Python 3 unified old-style and new-style classes so that every class implicitly inherits from the built-in object class. This made inheritance behavior more consistent and predictable.

The core question that inheritance answers is simple: how do we reuse existing code without duplicating it? And the follow-up question — how do we customize that reused code for specific needs? — is where method overriding enters the picture. Together, these two concepts let you build hierarchies of related classes that share a common foundation while each adding their own twist.

Core Principles & Definitions

Before writing any code, you need to understand a handful of foundational ideas. These principles explain what inheritance and method overriding are, why they matter, and how they connect to the broader world of object-oriented programming.

1

Parent (Base) Class

The class that holds shared attributes and methods. Other classes inherit from it. In Python, every class ultimately inherits from the built-in object class.
2

Child (Derived) Class

A class that inherits from a parent. It automatically gains all of the parent's attributes and methods, and it can add new ones or change existing ones.
3

Inheritance

The mechanism by which a child class receives code from a parent class. You declare it in Python by placing the parent's name in parentheses: class Child(Parent):
4

Method Overriding

When a child class defines a method with the same name as one in its parent, the child's version replaces the parent's version for that child. This is how you customize inherited behavior.
5

super()

A built-in Python function that lets a child class call the parent's version of a method. This is useful when you want to extend the parent's behavior rather than completely replace it.
✦ KEY TAKEAWAY
Think of inheritance like a family recipe. Your grandparent writes the original recipe (the parent class). You inherit that recipe automatically — you know every ingredient and step. Method overriding is when you decide to swap out one ingredient or change a step to suit your own taste, while keeping everything else the same. And super() is like saying, "Do everything grandma did, and then add my extra step on top."

Visual Explanation — Class Hierarchy Diagram

The diagram below shows a classic inheritance hierarchy. A parent class called Animal defines shared attributes (name, sound) and a method called speak(). Two child classes — Dog and Cat — inherit from Animal and override speak() to produce their own unique output.

The Animal parent class (purple border) defines speak(). The Dog and Cat child classes each override speak() (marked with ✦) to produce different output. Both children also add their own unique attributes.

Notice the arrows pointing from the parent to the children. In UML (Unified Modeling Language) diagrams used by professional developers, these arrows are drawn the other way — from child to parent — to show that the child "depends on" the parent. For learning purposes, the downward arrows here emphasize that behavior flows from parent to child. Each child automatically receives name and sound without re-declaring them, but each child overrides speak() to customize it.

How Inheritance & Overriding Work Under the Hood

The Method Resolution Order (MRO)

When you call a method on an object, Python doesn't just look in the object's class. It follows a specific search path called the Method Resolution Order (MRO). Python first checks the object's own class for the method. If it doesn't find the method there, it moves up to the parent class, then the grandparent class, and so on until it either finds the method or raises an AttributeError. This is why overriding works: because Python finds the child's version of a method before it ever reaches the parent's version.

Syntax for Inheritance

INHERITANCE SYNTAX
class ChildClass(ParentClass):
ChildClass — the new class being created. ParentClass — the existing class placed in parentheses. The child automatically inherits every attribute and method from the parent.

Syntax for Method Overriding

OVERRIDING PATTERN
def method_name(self): # same name as in parent
Define a method inside the child class with exactly the same name as the parent's method. Python will use the child's version whenever this method is called on a child object.

Using super() to Extend, Not Replace

SUPER() PATTERN
super().__init__(args) # calls parent's __init__
super() returns a temporary object of the parent class, allowing you to call the parent's method. This is especially common in __init__ so the child can set up the parent's attributes and then add its own.
💡 Why super() Matters
Without super(), a child's __init__ would completely replace the parent's __init__, meaning none of the parent's attributes would be created. Using super().__init__() ensures the parent's setup code runs first, and then the child adds its own attributes on top.

Detailed Code Walkthrough

Let's look at a complete, runnable Python example that demonstrates inheritance and method overriding step by step. The diagram below traces what happens when you create a Dog object and call its speak() method.

This diagram traces the five steps of creating a Dog object and calling speak(). The green dashed line shows super() reaching up to the Animal parent class. Step 5 demonstrates the MRO: Python finds Dog.speak() before it ever checks Animal.speak().

Pay close attention to step 2 in the diagram. When Dog.__init__ calls super().__init__(name, "Woof"), it passes "Woof" as the sound argument. This means every Dog automatically gets sound = "Woof" without the caller needing to specify it. The child class simplifies the interface while still relying on the parent to do the heavy lifting.

Summary of attributes and methods available on a Dog object
Attribute / MethodDefined InAvailable On Dog?
nameAnimal (parent)✅ Yes — inherited
soundAnimal (parent)✅ Yes — inherited
breedDog (child)✅ Yes — own attribute
speak()Both (overridden in Dog)✅ Uses Dog's version

Worked Example — Building a Vehicle Hierarchy

Let's build a fresh example from scratch to solidify your understanding. We'll create a Vehicle parent class and an ElectricCar child class that overrides a method.

Vehicle Inheritance Example
1
Step 1 — Define the Parent ClassWe start by creating a Vehicle class with an __init__ method that stores make, model, and year. We also add a fuel_type() method that returns "Gasoline" by default.
class Vehicle: with __init__ and fuel_type()
2
Step 2 — Define the Child Class with InheritanceWe write class ElectricCar(Vehicle): to make ElectricCar a child of Vehicle. In its __init__, we call super().__init__(make, model, year) so the parent's setup runs first. Then we add a new attribute, battery_kwh.
super().__init__(make, model, year) inherits parent setup; self.battery_kwh is new
3
Step 3 — Override the fuel_type() MethodInside ElectricCar, we define fuel_type(self) and return "Electric" instead of "Gasoline". Because the method has the same name as the parent's, Python will use this version for all ElectricCar objects.
fuel_type() now returns "Electric" for ElectricCar instances
4
Step 4 — Create an Object and TestWe create an object: my_car = ElectricCar("Tesla", "Model 3", 2024, 75). When we call my_car.fuel_type(), Python checks ElectricCar first, finds the overridden method, and returns "Electric". The Vehicle version is never reached.
my_car.fuel_type() → "Electric"
5
Step 5 — Verify Inherited AttributesWe can also access my_car.make, my_car.model, and my_car.year — all inherited from Vehicle via super(). Plus, my_car.battery_kwh equals 75, which is unique to the child class.
All parent attributes plus child-specific battery_kwh = 75 are available

Strengths, Limitations & Common Pitfalls

Inheritance is a powerful tool, but like any tool it can be misused. Understanding both its strengths and its pitfalls will help you decide when to use it and when a different approach might be better.

Strengths vs. limitations of inheritance
Strengths ✅Limitations / Pitfalls ⚠️
Code Reuse — Write shared logic once in the parent; all children get it automatically.Tight Coupling — Changing the parent can break child classes if they depend on specific internal details.
Consistency — Related classes share the same interface, making code predictable.Deep Hierarchies — A chain of five or more parent-child levels becomes confusing and hard to debug.
Extensibility — Add new child classes without modifying the parent.Fragile Base Class — A small change in the parent's method signature can silently break overriding in children.
Polymorphism — Different child objects can be used interchangeably wherever the parent type is expected.Forgetting super() — If you override __init__ without calling super(), parent attributes won't be initialized.
🔧 DESIGN GUIDELINE
A good rule of thumb is the "is-a" test. Only use inheritance when the child truly "is a" type of the parent. A Dog is-a Animal — that works. But a Wheel is-not-a Car; a Car has Wheels. If the relationship is "has-a" instead of "is-a", use composition (storing objects as attributes) rather than inheritance.

Connection to Advanced OOP Concepts

The single-inheritance pattern you learned today is just the beginning. As you continue studying Python, you'll encounter more advanced techniques that build directly on these foundations. Here is a quick preview of how today's concepts connect to what comes next.

How today's concepts connect to advanced OOP topics
What You Know NowWhat Comes NextKey Difference
Single inheritance (one parent)Multiple inheritance (two or more parents)A child can inherit from several classes at once: class C(A, B):
Method overriding (replace parent method)Abstract methods (force child to override)Using ABC module, the parent can require children to implement a method.
Using super() in __init__MRO with super() in diamond hierarchiesWhen multiple parents share a grandparent, Python's C3 linearization determines the call order.
Inheritance ("is-a" relationship)Composition ("has-a" relationship)Instead of inheriting, a class stores other objects as attributes. Often preferred for flexibility.

Don't worry about mastering all of these right now. The important thing is that single inheritance and method overriding form the foundation for every one of these advanced patterns. Once you feel confident creating parent and child classes, overriding methods, and using super(), you'll be ready to tackle any of these topics.

Practice Problems

PROBLEM 1 — CONCEPTUAL
In your own words, explain the difference between inheriting a method and overriding a method. Why would a child class want to override a method rather than simply use the parent's version?
PROBLEM 2 — BASIC CALCULATION
Given the following code, what will be printed? class Shape: def area(self): return 0 class Square(Shape): def __init__(self, side): self.side = side def area(self): return self.side * self.side s = Square(5) print(s.area())
PROBLEM 3 — INTERMEDIATE
Write a Student class that inherits from a Person class. Person has __init__(self, name, age) and a method greet() that returns "Hi, I'm [name]." The Student class should add a grade attribute and override greet() to return "Hi, I'm [name] and I'm in grade [grade]." Use super() in __init__.
PROBLEM 4 — APPLIED
You are building a game. You have a Character class with __init__(self, name, hp) and a method attack(self) that returns 10 (base damage). Create a Mage subclass that adds a mana attribute and overrides attack() so it returns 10 + self.mana // 5 (base damage plus a bonus based on mana, using integer division). If a Mage has 60 mana, what does attack() return?
PROBLEM 5 — CRITICAL THINKING
A classmate writes a Penguin class that inherits from a Bird class. The Bird class has a fly() method that returns "Soaring through the sky!" Your classmate overrides fly() in Penguin to return "I can't fly!" Is this a good use of inheritance and overriding? What design problems might this create? Suggest an alternative approach.

Lesson Summary

Inheritance lets a child class automatically receive all the attributes and methods of a parent class, enabling code reuse and consistent interfaces. You declare it by writing class Child(Parent):. Method overriding allows a child class to replace an inherited method with its own version by defining a method with the same name. Python's Method Resolution Order (MRO) searches the child class first, which is why the override takes priority.

The built-in super() function is essential when you want to extend rather than fully replace a parent's method — especially in __init__ where the parent sets up shared attributes. Always apply the "is-a" test before using inheritance: if the child truly "is a" type of the parent, inheritance is appropriate. With these tools, you can build clean, organized class hierarchies that are easy to read, maintain, and extend.

Varsity Tutors • Python • Inheritance & Method Overriding