On this page
Before you start
This lesson uses ideas from these earlier lessons. Read them first if any idea is new to you.
01Why interviewers ask this
When you build a class, you must decide how it reuses another class. There are two ways. The new class can be a kind of the old class, which is called inheritance. Or the new class can hold the old class as a part. Interviewers ask about the choice often: "Would you use inheritance here?" The answer shows whether you can think about change.
A weak answer uses inheritance everywhere, because it feels like reuse. After the second new requirement the classes multiply. A change in the old class then breaks the new ones in ways nobody expected. A strong answer asks two questions first and picks the lighter tool. It can also name the case where inheritance is the better tool.
At SDE-1 level you should know the difference and use inheritance for real "kind of" links. At SDE-2 level you should explain how inheritance multiplies classes and how holding an object avoids it.
02The idea in plain words
A samosa is a kind of snack. A canteen bill has a way of paying. The first is an "is a" link. The second is a "has a" link. A bill is not a kind of payment, and a payment is not a kind of bill. They are two separate things that work together.
This gives the test. Ask: "Is it a kind of that?" If yes, inheritance may fit. Ask: "Does it have one?" If yes, hold it as a field. When unsure, hold it.
Parent, child, override and extend
Inheritance lets one class take everything from another. The class you take from is the parent class. The class that takes is the child class. You write the parent in brackets: class Samosa(Snack):. The child has every field and method of the parent.
A child can change a method in two ways. To override a method is to write a new version that replaces the parent's version. To extend a method is to write a new version that first calls the parent's version and then adds more. The call is super().label(), where super() means "the parent's part of this object".
class Snack:
def label(self):
return "snack"
class Samosa(Snack):
def label(self):
return super().label() + ": samosa"
print(Samosa().label())This prints snack: samosa. Inheritance is a strong link. The child depends on the parent. If someone changes the parent, every child can break. A child can also be unable to keep a promise that its parent makes. A later section shows this.
Subclass explosion
Our canteen has two things that differ. The first is the member kind. A student gets 10 percent off. A staff member gets Rs 30 off when the bill is at least Rs 100. A guest gets nothing. Here Student is a kind of member, not the card type of the last lesson. The second is the way to pay: card, UPI or cash.
With inheritance you show a difference by making a child class. To show both differences you need one child class for each pair. The symbol // divides and drops the part after the point: 140 * 10 // 100 is 14.
class BaseOrder:
def __init__(self, total):
self.total = total
class StudentCardOrder(BaseOrder):
def due(self):
return self.total - self.total * 10 // 100
def pay(self, balance):
return self.due() <= balance
class StudentUpiOrder(BaseOrder):
def due(self):
return self.total - self.total * 10 // 100
def pay(self, limit_left):
return self.due() <= limit_leftThese are two of the classes we would need. The discount rule for students is copied into every student class. This is subclass explosion: two ideas that vary give kinds x ways classes, and every new way adds one class for each kind. You could put the student rule into a StudentOrder between the two levels. You would still need a class for every pair.
Composition and delegation
Composition means that a class holds other objects as fields and uses them. To delegate is to ask the held object to do the work. The order holds a member and a payment method. It asks the member for the discount and asks the payment method to pay. The two ideas now vary on their own, in their own classes. You can put in any part.
We use PaymentMethod, CardPayment, UpiPayment, CashPayment and VoucherPayment from the last lesson, and the CanteenCard of the lesson before it. The new part is the member. It has one interface and one small class for each kind.
from abc import ABC, abstractmethod
class Member(ABC):
@abstractmethod
def discount(self, total):
"""Return the rupees taken off total. Never more than total."""
class Student(Member):
def discount(self, total):
return total * 10 // 100Staff members and guests follow the same pattern.
class Staff(Member):
def discount(self, total):
if total >= 100:
return 30
return 0
class Guest(Member):
def discount(self, total):
return 0Now the order that holds both parts.
class Order:
def __init__(self, total, member, payment):
self._total = total
self._member = member
self._payment = payment
def due(self):
return self._total - self._member.discount(self._total)
def settle(self):
due = self.due()
if self._payment.pay(due):
return f"paid Rs {due}"
return f"refused Rs {due}"Look at the two links. Student is a kind of Member, so inheritance is right there. Order is not a kind of member or payment method. It has one of each.
A child must work wherever its parent works
Here is the plain rule for inheritance. Code that works with the parent must also work with every child, with no surprise. If a child cannot do what its parent promises, the link is wrong. See a child that breaks the promise of PaymentMethod:
class LeakyPayment(PaymentMethod):
def __init__(self, balance):
self._balance = balance
def pay(self, amount):
if amount > self._balance:
raise ValueError("not enough balance")
self._balance -= amount
return TrueThe promise says "do not raise an error for a refusal". This child raises. Order.settle does not expect that, so it crashes. Before you make a child, ask whether it can keep every promise of the parent.
Depth and many parents
Keep inheritance with few levels. A chain of parent, child and grandchild is already a warning. Two levels are usually enough. This is a common guideline, not a rule of the language. Python also lets a class have many parents: class A(B, C). A mixin is a small class with a few helper methods that you add this way. Python must decide which parent to search first, and with many parents that order is hard to follow. In a round, avoid many parents. In Java, a class may have only one parent class (but many interfaces), so this problem does not arise there.
A good use: Template Method
There is one use of inheritance that fits very well. The parent writes a method with fixed steps, in a fixed order. One step is left abstract. Each child fills that one step. This is called Template Method. The parent writes the order, and a child fills only one step, so a child has no reason to touch the order. (In Java you can write final to forbid a change. Python relies on the habits of the team.) In a text, "\n" is a line break.
class Bill(ABC):
def render(self, lines, total):
text = self.header() + "\n"
for line in lines:
text += line + "\n"
return text + f"Total Rs {total}\n" + self.footer()
def header(self):
return "CANTEEN BILL"
@abstractmethod
def footer(self):
"""The one step each child fills in."""
class StudentBill(Bill):
def footer(self):
return "Student discount 10% applied"render fixes the steps: the header, the lines, the total, the footer. StudentBill fills the footer only.
Want to check your understanding? Try explaining this idea in your own words. You can continue reading first, or ask the AI tutor a question. Sign up free to ask the tutor
03Pause and predict
Pause. Guess first.
The canteen has 3 member kinds and 3 ways to pay. With one class for every pair, how many classes are there? A voucher is added as a fourth way to pay. How many new classes are needed with pairs, and how many if an order holds a member and a payment method?
Write your guess before you read on. The answer is in the worked example.
04Worked example
Counting the classes.
| Design | At the start | After the voucher |
|---|---|---|
| One class for every pair | 3 x 3 = 9 | 9 + 3 = 12 |
| Order holds a member and a payment method | 3 + 3 = 6 | 6 + 1 = 7 |
With pairs, the voucher adds one class for each member kind: 3 new classes. With composition it adds 1 payment class. This answers the predict box: 3 new classes against 1. The gap grows. With 5 kinds and 5 ways, pairs give 5 x 5 = 25 classes and composition gives 5 + 5 = 10.
Running the composed design. Assumption: the bill is Rs 140. The student card holds Rs 200, the UPI payment has Rs 100 of its limit left, and the cash given is Rs 150. Run with Python 3.11.
card = CanteenCard("C101", CardType.STUDENT, 200)
print(Order(140, Student(), CardPayment(card)).settle(), card.balance)
print(Order(140, Staff(), UpiPayment(100)).settle())
print(Order(140, Guest(), CashPayment(150)).settle())It prints paid Rs 126 74, then refused Rs 110, then paid Rs 140. The arithmetic:
- Student and card: the discount is 140 x 10 / 100 = Rs 14, so the amount due is 140 - 14 = Rs 126. The card goes from 200 to 200 - 126 = Rs 74.
- Staff and UPI: the bill is at least 100, so the discount is Rs 30. The amount due is 140 - 30 = Rs 110. This is more than the UPI limit of Rs 100, so it is refused.
- Guest and cash: no discount, so the amount due is Rs 140. The cash given, Rs 150, is enough.
The voucher. It is a payment class from the last lesson, and it works with all three member kinds, because Order does not change.
print(Order(140, Student(), VoucherPayment(150)).settle())It prints paid Rs 126: 126 is not more than 150.
The broken promise. Use the leaky class in the same order:
try:
print(Order(140, Guest(), LeakyPayment(100)).settle())
except ValueError as error:
print("crash:", error)It prints crash: not enough balance. The amount due is Rs 140, the balance is Rs 100, and the child raised where the parent promised not to.
Template Method.
print(StudentBill().render(["Thali Rs 90", "Lassi Rs 50"], 126))It prints five lines: CANTEEN BILL, Thali Rs 90, Lassi Rs 50, Total Rs 126 and Student discount 10% applied.
05Trade-offs
Composition solves the explosion: classes add up, each part changes on its own, and you can put in any part, even in a test. It costs a few more lines, because the order must pass each request to the part, and the reader must follow one more object. Inheritance solves repeated code for real "kind of" links and gives Template Method. It costs a strong link: a change in the parent can break every child. I would change to inheritance in one case. The child must be truly a kind of the parent, keep all its promises, and the parent must fix a sequence of steps. If kinds differ only by a number, I would store the number in a field and write no new class.
06Common misconceptions
"Inheritance is the main way to reuse code." It is one way, and a strong link. Holding an object reuses the same code without tying your class to a parent.
"A child is always safe, because it is a kind of its parent." The words can say yes while the code says no. LeakyPayment is a payment method by name, but it breaks the promise and crashes the order.
"Prefer composition means never use inheritance." It means start with the lighter tool. Interfaces and Template Method are good uses of inheritance.
07How to say it in the interview
Say it like this
I ask whether it is a kind of that or has one. When two things vary, I make the order hold both parts, so the classes add up instead of multiplying.
08Check your understanding
- Use the test "is it a kind of that, or does it have one?" on three pairs. (a)
SamosaandSnack. (b)OrderandMember. (c)OrderandPaymentMethod. - A canteen has 4 member kinds and 2 ways to pay. How many classes do you need (a) with one class for every pair, and (b) with composition? A fifth member kind is added. How many new classes does each design need?
- A teammate writes
class CashPayment(CardPayment)to reuse a balance check, and overridespay. What is wrong, and what do you do?
09Answers
Show the answers
- (a) A samosa is a kind of snack: inheritance fits. (b) An order has a member: hold it. (c) An order has a payment method: hold it.
- (a) Pairs: 4 x 2 = 8 classes. (b) Composition: 4 + 2 = 6 classes. A fifth member kind adds one class for each way to pay with pairs: 2 new classes, so 10. With composition it adds 1 class, so 7.
- Cash is not a kind of card.
CashPaymentcarries card fields it does not use, and a change inCardPaymentcan break it. Make both implementPaymentMethod. If both need the same check, put it in a small helper that both call.
10Summary card: composition or inheritance
One-page summary
- Test. "Is it a kind of that?" means inheritance may fit. "Does it have one?" means hold it as a field. When unsure, hold it.
- Words. Override replaces a parent method. Extend calls
super()and adds more. Delegate means ask the held part to do the work.- Explosion. Two ideas that vary give kinds x ways classes with inheritance, and kinds + ways with composition. Example: 3 x 3 = 9 against 3 + 3 = 6.
- Rule. A child must work wherever its parent works. Do not break the parent's promise.
- Depth. Two levels is a warning. Avoid many parents and mixins.
- Good use. Template Method: the parent fixes the steps, the child fills one step.
- Trap. Using inheritance only to reuse code.
11Sources
super(), many parents and the order in which Python searches them: Python tutorial, section 9.5 "Inheritance", https://docs.python.org/3/tutorial/classes.html (opens in a new tab) (checked 29 Oct 2026).- Template Method (the parent fixes the steps and the children fill some of them): Refactoring Guru, https://refactoring.guru/design-patterns/template-method (opens in a new tab) (checked 29 Oct 2026). The name comes from the book "Design Patterns" by Gamma, Helm, Johnson and Vlissides (1994).
- The advice that two levels of inheritance are usually enough, and that mixins are best avoided in a round, is a common guideline and not from a source.
- The canteen, the prices, the discounts and the limits are made up for teaching. All code was run with Python 3.11.
Try what you just learned
Put this idea into practice with a short exercise or a relevant tool. Free to try. No sign-up needed.
More practice. This needs a free account.
What to learn next
Continue learning with the lessons that build on this one.
- Encapsulation: keep the data safe inside the class
- From requirements to classes, with just enough UML
- Classes and objects: data and actions in one place
Practise this idea
Have a question about this lesson?
Ask the AI tutor to explain a difficult idea, walk through an example or check your understanding. It uses the lesson to help you work through the topic.
A free account includes 10 AI tutor questions a day. The lesson remains free to read without an account.
All lessons in Module 2: Low-level design and machine coding
Prefer another topic? Browse all 169 lessons.