Python Mastery — Phase 3: Object-Oriented Programming Inheritance
Python Mastery
Phase 3, Lesson 12 — Inheritance
A complete beginner's guide · Written for 10th grade and up
What you learned in Lesson 11: classes and objects — class definitions, __init__, self, instance and
class attributes, and instance methods. Today you learn inheritance — how one class can build on
another, getting all its features for free and adding or changing what it needs.
1. What is inheritance?
Inheritance lets a child class automatically receive all the attributes and methods of a parent class. The
child can then add new features or change existing ones — without touching or duplicating the parent's
code.
Real-life analogy
Think of a job description at a company.
'Manager' inherits all the responsibilities of 'Employee' — name, ID, salary, clock_in().
'Manager' then adds extra responsibilities: approve_leave(), performance_review().
You don't rewrite the Employee description inside Manager. You just say 'Manager is an
Employee, plus...'
The problem inheritance solves
# Without inheritance — repeated code
class Dog:
def __init__(self, name, age):
[Link] = name
[Link] = age
def describe(self):
return f'{[Link]}, age {[Link]}'
class Cat:
def __init__(self, name, age): # IDENTICAL to Dog
[Link] = name
[Link] = age
def describe(self): # IDENTICAL to Dog
return f'{[Link]}, age {[Link]}'
# With inheritance — write once, reuse everywhere
class Animal:
def __init__(self, name, age):
[Link] = name
[Link] = age
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 1
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
def describe(self):
return f'{[Link]}, age {[Link]}'
class Dog(Animal): # inherits everything
def fetch(self): # adds new behaviour
print(f'{[Link]} fetches!')
class Cat(Animal):
def purr(self):
print(f'{[Link]} purrs...')
2. Key terms and concepts
Term Meaning Example
Parent class The class being inherited from class Animal:
Child class The class that inherits class Dog(Animal):
Inheritance Child gets all parent attrs and Dog gets name, age, describe()
methods
super() Calls the same method on the super().__init__(name, age)
parent
Override Child redefines a method from the def speak(self): return
parent 'Woof!'
Polymorphism Same method call, different [Link]() -> 'Woof!' or
behaviour 'Meow!'
isinstance() True if obj is that class or any parent isinstance(dog, Animal) ->
True
issubclass() True if class inherits from another issubclass(Dog, Animal) ->
True
What a child class inherits
What Inherited? Notes
Instance attributes (self.x) Yes Child calls super().__init__() to get them
Instance methods Yes Child can override any method by redefining
it
Class attributes Yes Shared — child can access [Link]
__init__ Yes Override by defining your own __init__ +
super()
Private attrs (__x) No Name-mangled — not accessible in child
directly
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 2
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
3. Basic inheritance — using the parent as-is
The simplest form of inheritance: the child class adds new methods but uses everything from the parent
unchanged. The parent's __init__ runs automatically if the child has no __init__ of its own.
class Animal:
def __init__(self, name, age):
[Link] = name
[Link] = age
def describe(self):
return f'{[Link]}, age {[Link]}'
class Dog(Animal): # inherits __init__ and describe()
def fetch(self): # Dog's own method
print(f'{[Link]} fetches the ball!')
class Cat(Animal):
def purr(self):
print(f'{[Link]} purrs...')
dog = Dog('Rex', 3) # uses Animal's __init__
print([Link]()) # Rex, age 3 — Animal's method
[Link]() # Rex fetches the ball!
cat = Cat('Bella', 5)
print([Link]()) # Bella, age 5
[Link]() # Bella purrs...
4. super() — calling the parent's methods
When a child class needs its own __init__ (because it has extra attributes), it must also call the parent's
__init__ to set up the inherited attributes. super() is how you do this cleanly.
Always call super().__init__() first
Call super().__init__() as the FIRST line inside your child's __init__.
This runs the parent's __init__, setting up all inherited attributes.
Then add your child's extra attributes below it.
If you forget super(), attributes like [Link] and [Link] are never created.
super().__init__() in action
class Animal:
def __init__(self, name, age):
[Link] = name
[Link] = age
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # sets [Link], [Link]
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 3
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
[Link] = breed # Dog's extra attribute
def describe(self):
return f'{[Link]} ({[Link]}), age {[Link]}'
dog = Dog('Rex', 3, 'Labrador')
print([Link]) # Rex — set by Animal.__init__
print([Link]) # Labrador — set by Dog.__init__
print([Link]()) # Rex (Labrador), age 3
super() in other methods — not just __init__
class Animal:
def describe(self):
return f'Name: {[Link]}, Age: {[Link]}'
class Dog(Animal):
def describe(self):
base = super().describe() # get parent's text
return base + f', Breed: {[Link]}' # add to it
dog = Dog('Rex', 3, 'Labrador')
print([Link]())
# Name: Rex, Age: 3, Breed: Labrador
5. Method overriding and polymorphism
When a child class defines a method with the same name as one in the parent, the child's version
replaces the parent's version for that class. This is called method overriding. When different child
classes override the same method differently, it is called polymorphism.
Pattern Use when Code
No override Parent version is fine as-is class Dog(Animal): pass
Full override Child needs completely def speak(self): return
different logic 'Woof!'
Extend with super() Child wants parent's output + base = super().method();
more return base + '...'
Polymorphism in action
class Animal:
def __init__(self, name):
[Link] = name
def speak(self): # parent — generic
return f'{[Link]} makes a sound.'
class Dog(Animal):
def speak(self): # overrides parent
return f'{[Link]} says: Woof!'
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 4
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
class Cat(Animal):
def speak(self): # overrides parent
return f'{[Link]} says: Meow!'
class Fish(Animal):
pass # no override — uses parent
animals = [Dog('Rex'), Cat('Bella'), Fish('Nemo')]
for a in animals:
print([Link]())
# Output:
# Rex says: Woof!
# Bella says: Meow!
# Nemo makes a sound.
Polymorphism — the big idea
The same line of code — [Link]() — does different things depending on the type of a.
You do not need to check what type a is. You just call speak() and Python figures it out.
This lets you write one loop that handles Dog, Cat, Fish, and any future animal you add.
You never have to change the loop — just add a new class with a speak() method.
6. isinstance() and issubclass()
These two built-in functions let you check class relationships at runtime. isinstance() checks whether an
object belongs to a class or any of its parents. issubclass() checks whether one class inherits from
another.
class Animal: pass
class Dog(Animal): pass
class Cat(Animal): pass
rex = Dog()
# isinstance — checks object type (includes parent classes)
print(isinstance(rex, Dog)) # True — rex is a Dog
print(isinstance(rex, Animal)) # True — Dog inherits from Animal
print(isinstance(rex, Cat)) # False — rex is not a Cat
# issubclass — checks class hierarchy
print(issubclass(Dog, Animal)) # True — Dog inherits Animal
print(issubclass(Cat, Dog)) # False — Cat does not inherit Dog
print(issubclass(Animal, Animal))# True — a class is a subclass of itself
Practical use — filtering by type
animals = [Dog('Rex'), Cat('Bella'), Dog('Max'), Cat('Luna')]
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 5
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
# Filter only dogs
dogs = [a for a in animals if isinstance(a, Dog)]
print(f'Dogs: {len(dogs)}') # Dogs: 2
# Call type-specific methods safely
for a in animals:
if isinstance(a, Dog):
[Link]() # only dogs have fetch()
7. Full worked example — Bank account hierarchy
This is the most important example in the lesson. BankAccount is the parent. SavingsAccount and
CurrentAccount are children that inherit the core logic but add or override behaviour as needed.
class BankAccount:
def __init__(self, owner, balance=0):
[Link] = owner
[Link] = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError('Deposit must be positive')
[Link] += amount
print(f'Deposited {amount}. Balance: {[Link]}')
def withdraw(self, amount): # parent version
if amount > [Link]:
print('Insufficient funds')
else:
[Link] -= amount
print(f'Withdrew {amount}. Balance: {[Link]}')
def statement(self):
print(f'{type(self).__name__} — {[Link]}: Rs {[Link]}')
class SavingsAccount(BankAccount):
def __init__(self, owner, balance=0, rate=0.04):
super().__init__(owner, balance) # set owner and balance
[Link] = rate # extra: interest rate
def add_interest(self): # new method
interest = [Link] * [Link]
[Link] += interest
print(f'Interest added: Rs {interest:.2f}')
class CurrentAccount(BankAccount):
def __init__(self, owner, balance=0, overdraft=500):
super().__init__(owner, balance)
[Link] = overdraft
def withdraw(self, amount): # OVERRIDES parent withdraw
limit = [Link] + [Link]
if amount > limit:
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 6
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
print('Overdraft limit exceeded')
else:
[Link] -= amount
print(f'Withdrew {amount}. Balance: {[Link]}')
# Using the hierarchy
savings = SavingsAccount('Arun', 10000)
current = CurrentAccount('Priya', 500, overdraft=1000)
[Link](2000)
savings.add_interest() # 4% of 12000 = 480
[Link]()
[Link](900) # allowed — uses overdraft
[Link]()
print(isinstance(savings, BankAccount)) # True
print(isinstance(current, SavingsAccount)) # False
# Output:
# Deposited 2000. Balance: 12000
# Interest added: Rs 480.00
# SavingsAccount — Arun: Rs 12480
# Withdrew 900. Balance: -400
# CurrentAccount — Priya: Rs -400
# True
# False
8. Common mistakes
Mistake 1 — Forgetting super().__init__() in the child
class Dog(Animal):
def __init__(self, name, age, breed):
[Link] = breed # name and age are NEVER SET
Fix: call super().__init__(name, age) as the FIRST line.
Mistake 2 — Passing self to super().__init__()
Wrong: super().__init__(self, name, age) <- TypeError, extra argument
Right: super().__init__(name, age) <- self is passed automatically
super() already knows which object to use. Never pass self to it.
Mistake 3 — Overriding without calling super() when you need parent behaviour
If you override a method and also need the parent's logic, call super().method().
Without it, the parent's logic is completely replaced — sometimes that's right,
sometimes you lose important setup. Think before you override.
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 7
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
Mistake 4 — Using type(obj) == Dog instead of isinstance()
type(dog) == Dog is True but type(dog) == Animal is False.
isinstance(dog, Animal) correctly returns True because of inheritance.
Always use isinstance() — it respects the inheritance chain.
9. Practice exercises
All four exercises build real class hierarchies. Exercise 4 is the most complete — a full OOP
application.
Exercise 1 — Shape hierarchy
Create a parent Shape class with colour and a describe() method. Create Circle (adds radius, overrides
area() and perimeter()) and Rectangle (adds width and height, overrides area() and perimeter()). Use
[Link] for circles.
Exercise 2 — Employee hierarchy
Create an Employee parent class with name, id, and salary. Create Manager (adds team_size,
overrides describe() to include team info) and Developer (adds language, overrides describe()). Both
call super().describe() and extend it.
Exercise 3 — Vehicle hierarchy
Create a Vehicle parent with make, model, year, and a start() method. Create ElectricVehicle (adds
battery_range, overrides start() to say 'silent start') and PetrolVehicle (adds fuel_type, overrides start()
with engine noise). Use isinstance() to count EVs in a list.
Exercise 4 — Animal shelter
Build an animal shelter. Animal is the parent (name, age, adopted=False). Dog and Cat inherit from it.
Dog adds breed and can_fetch. Cat adds indoor_only. Add an adopt() method to Animal. Write a
shelter function that prints all unadopted animals, grouped by type using isinstance().
10. Quick reference card
# === basic inheritance ===
class Parent:
def __init__(self, x):
self.x = x
def method(self):
return self.x
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 8
Python Mastery — Phase 3: Object-Oriented Programming Inheritance
class Child(Parent): # inherit from Parent
def extra(self): # new method
return self.x * 2
# === child with own __init__ ===
class Child(Parent):
def __init__(self, x, y):
super().__init__(x) # ALWAYS call this first
self.y = y # then add child attributes
# === method overriding ===
class Child(Parent):
def method(self): # replaces parent's method
return self.x + 100
# === extend parent method ===
class Child(Parent):
def method(self):
base = super().method() # get parent's result
return base + ' extra' # add to it
# === isinstance and issubclass ===
isinstance(obj, Class) # True if obj is Class or child
issubclass(Child, Parent) # True if Child inherits Parent
# === polymorphism pattern ===
objects = [Dog('Rex'), Cat('Bella'), Fish('Nemo')]
for o in objects:
print([Link]()) # each calls its own version
What comes next?
You have completed Lesson 12 of Phase 3. Two lessons remain before the capstone:
• Lesson 13 — Special methods (dunder methods): __str__, __len__, __add__, __eq__ —
making your objects respond to print(), len(), + and == like built-in types
• Lesson 14 — Modules, packages, and list comprehensions
• Lesson 15 — Phase 3 capstone: a full OOP bank account system
YouTube tip
Record yourself building the BankAccount hierarchy — parent first, then both children.
Show what happens when you forget super().__init__() — the AttributeError is the content.
Then show the polymorphism loop: one line of code, three different outputs.
Title idea: 'Python inheritance explained by building a real bank — 10 minutes.'
Inheritance means never having to write the same code twice. Build the parent right, and everything
else follows.
Beginner's Python Notes | Phase 3 of 5 — Lesson 12 | Page 9