Single Inheritance and Method Overriding
15 min · Beginner
Need help? Ask a tutor on WhatsApp (opens in a new tab)Inheritance allows a new class (subclass or child class) to inherit attributes and methods from an existing class (base class or parent class). It enables code reuse and models "is-a" relationships.
Single Inheritance Syntax
Inheritance is a way to build a new class (the child) on top of an existing class (the parent). The child gets the parent's attributes and methods and can add or change its own.
To inherit from a parent class, put the parent class name in parentheses after the child class name.
class Vehicle: def __init__(self, brand): self.brand = brand
def move(self): return "Moving forward"
class Bicycle(Vehicle): def ring_bell(self): return "Ring ring!"
bike = Bicycle("Trek")print(bike.brand) # Inherited attributeprint(bike.move()) # Inherited methodprint(bike.ring_bell()) # Subclass methodExample 1 · A child reuses and extends a parent
class Employee: def __init__(self, name, salary): self.name = name self.salary = salary
def monthly_pay(self): return self.salary / 12
class Manager(Employee): def monthly_pay(self): return self.salary / 12 + 5000
staff = Employee("Ravi", 600000)lead = Manager("Anita", 900000)print(staff.name, staff.monthly_pay())print(lead.name, lead.monthly_pay())Ravi 50000.0 Anita 80000.0
What this code does
Managerhas no__init__, so it uses the one fromEmployee.Managerreplaces onlymonthly_pay(). Everything else is inherited.
Bicycle inherits all public and protected attributes and methods from Vehicle.
Method Overriding
Method overriding is writing a method in a child class with the same name as a method in its parent. Python then runs the child's version for child objects.
A child class can provide its own specialized implementation of a method defined in its parent.
class ElectricCar(Vehicle): def move(self): return "Cruising silently on battery power"
ev = ElectricCar("Tesla")print(ev.move())Example 2 · Overriding in a loop
class Animal: def speak(self): return "Some generic sound"
class Cat(Animal): def speak(self): return "Meow!"
animals = [Animal(), Cat()]for a in animals: print(a.speak())Some generic sound Meow!
What this code does
Catoverridesspeak().- Iterating over the list invokes the specialized method polymorphically.
Example 3 · The parent calls the child's override
class Report: def title(self): return "Report"
def render(self): return f"== {self.title()} =="
class SalesReport(Report): def title(self): return "Sales"
print(Report().render())print(SalesReport().render())== Report == == Sales ==
What this code does
render()is written once, in the parent, and callsself.title().- For a
SalesReport,selfis the child, so Python finds the child'stitle()first.
When ev.move() is called, Python finds the method in ElectricCar and executes it, overriding Vehicle.move().
Type Checking with isinstance and issubclass
Use isinstance(obj, Class) to check object types, and issubclass(Child, Parent) to check class hierarchies.
print(isinstance(ev, ElectricCar)) # Trueprint(isinstance(ev, Vehicle)) # True (is-a Vehicle)print(issubclass(ElectricCar, Vehicle)) # TrueExample 4 · isinstance follows the hierarchy, type does not
class Payment: pass
class CardPayment(Payment): pass
p = CardPayment()print(type(p) == Payment)print(isinstance(p, Payment))print(isinstance(p, (CardPayment, int)))False True True
What this code does
type(p)is exactlyCardPayment, so comparing it withPaymentgivesFalse.isinstanceaccepts a tuple of classes and is true if the object matches any of them.
isinstance respects the entire inheritance hierarchy, unlike direct type comparison type(obj) == Class.
The Liskov Substitution Principle (LSP)
Liskov Substitution Principle says code written for a parent class must keep working when it receives any child object. A child may add behaviour but must not break what the parent promised.
Code written for a parent must work with any child. A child keeps the same parameters, returns compatible values, and never removes a guarantee the parent gave.
Example 5 · A child that breaks the parent contract
class Discount: def apply(self, price): return price
class Seasonal(Discount): def apply(self, price): return price * 0.9
class Coupon(Discount): def apply(self, price, code): return price - 100
def checkout(discount, price): return discount.apply(price)
print(checkout(Seasonal(), 1000))try: print(checkout(Coupon(), 1000))except TypeError as error: print("Broke the contract:", error)900.0 Broke the contract: Coupon.apply() missing 1 required positional argument: 'code'
What this code does
checkoutwas written for anyDiscount, and it callsapply(price)with one argument.Coupondemands an extra required argument, so substituting it fails. That is a Liskov violation.
Next step
Check your understanding
11 quick questions