Inheritance
Many things in the real world are special versions of something more general. A sleeper ticket and an AC ticket are both train tickets; a UPI payment and a card payment are both payments. Inheritance lets you write the shared part once in a general class and then create special versions that reuse it and change only what is different.
In this lesson you will build subclasses, override methods, call the parent with super(), check types with isinstance(), and force subclasses to provide methods with abstract base classes.
Creating a subclass
The general class is called the parent, base or superclass. A class that inherits from it is the child or subclass. To inherit, put the parent's name in brackets after the class name. The child automatically gets every attribute and method of the parent.
class Ticket:
rate_per_km = 0.5
def __init__(self, passenger, distance_km):
self.passenger = passenger
self.distance_km = distance_km
def fare(self):
return round(self.distance_km * self.rate_per_km)
def summary(self):
kind = type(self).__name__
return f"{kind} for {self.passenger}: ₹{self.fare()}"
class SleeperTicket(Ticket):
rate_per_km = 0.9
class ACTicket(Ticket):
rate_per_km = 2.4
trip = [Ticket("Aarav", 300), SleeperTicket("Priya", 300), ACTicket("Kabir", 300)]
for ticket in trip:
print(ticket.summary())Ticket for Aarav: ₹150 SleeperTicket for Priya: ₹270 ACTicket for Kabir: ₹720
SleeperTicket and ACTicket have no __init__, fare() or summary() of their own. They inherit all three from Ticket and only change the class attribute rate_per_km. When fare() reads self.rate_per_km, Python looks at the object's own class first, so each ticket uses its own rate.
The loop also shows polymorphism: the same call, ticket.summary(), works on different classes, and each object answers in its own way.
Overriding methods and calling super()
A subclass can override a method by defining one with the same name. Often you do not want to replace the parent's work completely, only add to it. super() gives you access to the parent's version, so you can call it and then do the extra part.
The most common place for this is __init__, when a subclass needs extra attributes:
class Ticket:
rate_per_km = 0.5
def __init__(self, passenger, distance_km):
self.passenger = passenger
self.distance_km = distance_km
def fare(self):
return round(self.distance_km * self.rate_per_km)
class TatkalTicket(Ticket):
def __init__(self, passenger, distance_km, tatkal_charge):
super().__init__(passenger, distance_km)
self.tatkal_charge = tatkal_charge
def fare(self):
return super().fare() + self.tatkal_charge
normal = Ticket("Meera", 500)
urgent = TatkalTicket("Rohan", 500, 175)
print(normal.fare(), urgent.fare())
print(urgent.passenger, urgent.tatkal_charge)250 425 Rohan 175
super().__init__(passenger, distance_km) lets the parent set passenger and distance_km, so that code is not repeated. Then the child adds tatkal_charge. In fare(), super().fare() calculates the normal fare and the child adds the extra charge on top.
Checking types with isinstance()
isinstance(obj, SomeClass) returns True if the object belongs to that class or any of its subclasses. issubclass(Child, Parent) asks the same question about classes.
class Ticket:
pass
class TatkalTicket(Ticket):
pass
t = TatkalTicket()
print(isinstance(t, TatkalTicket))
print(isinstance(t, Ticket))
print(isinstance(Ticket(), TatkalTicket))
print(issubclass(TatkalTicket, Ticket))
print(issubclass(Ticket, object))True True False True True
The last line shows that every class in Python inherits from the built-in class object, even if you do not write it. That is where default behaviour such as printing <__main__.Ticket object at 0x...> comes from.
Prefer isinstance() over type(t) == Ticket, because it respects inheritance: a Tatkal ticket is a ticket.
Abstract base classes
Sometimes the parent class is only a template, and it makes no sense to create objects from it directly. A general "payment" has no way to pay; only UPI or card payments do. The abc module (abstract base classes) lets you mark such methods with @abstractmethod. Python then refuses to create an object of any class that has not provided them.
from abc import ABC, abstractmethod
class Payment(ABC):
def __init__(self, amount):
self.amount = amount
@abstractmethod
def pay(self):
"""Every payment type must say how it pays."""
class UpiPayment(Payment):
def __init__(self, amount, upi_id):
super().__init__(amount)
self.upi_id = upi_id
def pay(self):
return f"Paid ₹{self.amount} to {self.upi_id} by UPI"
class CardPayment(Payment):
def pay(self):
return f"Paid ₹{self.amount} by card"
for p in [UpiPayment(349, "chaiwala@okbank"), CardPayment(1299)]:
print(p.pay())
try:
Payment(100)
except TypeError as error:
print("Error:", error)Paid ₹349 to chaiwala@okbank by UPI Paid ₹1299 by card Error: Can't instantiate abstract class Payment without an implementation for abstract method 'pay'
Abstract classes act like a contract. If someone adds a WalletPayment class next month and forgets to write pay(), they get a clear error the moment they create one, not a confusing bug later.
Multiple inheritance and the MRO
A class can inherit from more than one parent: class C(A, B):. Python then needs a rule for which parent to search first. That order is called the method resolution order (MRO), and you can see it with ClassName.__mro__. Python searches the class itself, then the parents from left to right, and object last.
Multiple inheritance is mostly used for small mixins: classes that add one feature, such as a method that converts an object to JSON. Keep it simple; deep or tangled hierarchies are hard to follow.
Common mistakes
- Forgetting
super().__init__(). If a child defines its own__init__and does not call the parent's, the parent's attributes are never set, and you get anAttributeErrorlater. - Copying parent code into the child. If you paste a parent method and change one line, use
super()and add only the new part. - Using inheritance for "has a". An
Ordershould not inherit fromlist; give it anitemslist attribute. - Comparing types with
==.type(obj) == ParentisFalsefor subclasses; useisinstance().
Key points
- A subclass inherits all attributes and methods of its parent:
class Child(Parent):. - Override a method by defining it again in the child; call the parent's version with
super(). - Call
super().__init__(...)when a child adds its own__init__. isinstance()andissubclass()respect inheritance; every class inherits fromobject.ABCand@abstractmethodstop objects being created until subclasses implement required methods.- Use inheritance for "is a" relationships and composition for "has a".
Exercise
Create a base class Employee with name and monthly_salary, and a method annual_pay() that returns 12 times the salary. Create a subclass Manager that also takes bonus, calls super().__init__(), and overrides annual_pay() to add the bonus using super(). Create one employee and one manager from a Pune IT company and print each person's name and annual pay.
Show solution
class Employee:
def __init__(self, name, monthly_salary):
self.name = name
self.monthly_salary = monthly_salary
def annual_pay(self):
return self.monthly_salary * 12
class Manager(Employee):
def __init__(self, name, monthly_salary, bonus):
super().__init__(name, monthly_salary)
self.bonus = bonus
def annual_pay(self):
return super().annual_pay() + self.bonus
staff = [Employee("Ananya", 65000), Manager("Farhan", 120000, 200000)]
for person in staff:
print(f"{person.name}: ₹{person.annual_pay():,}")Ananya: ₹780,000 Farhan: ₹1,640,000
Manager reuses the parent's salary calculation through super().annual_pay() and only adds the bonus, so the yearly formula lives in one place.
Quick Quiz
1. How do you make Car inherit from Vehicle?
The parent class goes in brackets after the child's name.
2. What does super().__init__(name) do inside a child's __init__?
super() gives access to the parent's methods, so the parent's set-up code runs.
3. class Tatkal(Ticket) exists and t = Tatkal(). What is isinstance(t, Ticket)?
isinstance() counts subclasses, and a Tatkal ticket is a ticket.
4. What happens when you create an object of a class that still has an abstract method?
Classes with unimplemented abstract methods cannot be instantiated.
Learn Python with a mentor
Free lessons get you started. Our Python Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
