Object-Oriented Programming — Inheritance
The Problem: Repeated Structure
Suppose your application has different types of employees — regular employees, managers, and contractors. Each has a name and a salary. But managers also have a team. Contractors have an hourly rate and work a variable number of hours.
You could write three completely separate classes. But they would share a large amount of structure — the common attributes and methods would be duplicated across all three. Any change to the common parts would need to be made in three places.
Inheritance solves this. You define the shared behaviour once in a parent class (also called a base class or superclass), then create child classes (subclasses) that extend it.
A Parent Class
A basic Employee class. Now let's extend it.
Creating a Child Class
class Manager(Employee) — the parentheses declare the parent class. Manager inherits everything from Employee.
super().__init__(name, salary) — super() refers to the parent class. Calling super().__init__() runs the parent's __init__, setting up name and salary. Then self.team_size = team_size adds the new attribute specific to managers.
mgr.give_raise(10000) — Manager didn't define give_raise, but it inherited it from Employee. It works exactly the same.
Method Overriding
Manager defines its own describe() — this overrides the parent's version. When you call mgr.describe(), Python finds describe in Manager first and uses that instead of Employee's version.
This is intentional. The manager's description includes team size; the base employee's doesn't. Same method name, different behaviour — appropriate to the type.
Overriding is always this simple: define a method with the same name in the child class.
Extending a Parent Method
Sometimes you want to override a method but still include what the parent does — not replace it entirely.
Manager.give_raise() calculates a bigger amount, then calls super().give_raise() to let the parent handle the actual salary update and message. You extend the behaviour rather than rewrite it.
isinstance() — Checking Types
A child class instance is also an instance of the parent. A Manager is also an Employee — inheritance establishes an is-a relationship. isinstance() respects this hierarchy.
Polymorphism
Polymorphism means different types, same interface. When multiple classes share a method name, you can call that method on any of them without knowing or caring which specific type you have.
Employee and Contractor are completely separate classes with no inheritance relationship. But both define monthly_pay(). The loop calls monthly_pay() on each without knowing which type it's dealing with. Python finds the right version at runtime.
This is polymorphism — writing code that works with objects based on what they can do, not what they are.
A Complete Hierarchy
self.__class__.__name__ gives the actual class name of the object — "Dog", "Cat", "Duck" — without hardcoding it. A small but useful detail.
What You Have Learned
Inheritance lets you build specialised classes on top of general ones — reusing, extending, and replacing behaviour precisely where needed.
The key ideas:
class Child(Parent):declares inheritancesuper().__init__(...)runs the parent's constructor — always call it when overriding__init__- Method overriding: define a method with the same name in the child to replace the parent's version
super().method()calls the parent's version of a method from within the child- A child instance is also an instance of the parent —
isinstance()respects the hierarchy - Polymorphism: different classes sharing a method name, called uniformly through a common interface
- Inheritance establishes an is-a relationship — use it when a child genuinely is a specialised form of the parent
In the final lesson of this series, you'll learn about iterators and generators — how Python's for loop really works, and how to build sequences that produce values lazily, one at a time.