The big idea: Inheritance lets one class be defined as a specialised version of another. The child gets the parent's attributes and methods without repeating them, and adds what makes it different.
Write it once, change it in one place.
class Account:
def __init__(self, owner, balance):
self.owner = owner
self._balance = balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
class SavingsAccount(Account): # inherits everything above
def __init__(self, owner, balance, rate):
super().__init__(owner, balance) # reuse the parent's setup
self.rate = rate
def add_interest(self): # the specialisation
self.deposit(self._balance * self.rate)class Account {
String owner;
protected double balance;
Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
void deposit(double amount) {
if (amount > 0) {
this.balance += amount;
}
}
}
class SavingsAccount extends Account { // inherits everything above
double rate;
SavingsAccount(String owner, double balance, double rate) {
super(owner, balance); // reuse the parent's setup
this.rate = rate;
}
void addInterest() { // the specialisation
this.deposit(this.balance * this.rate);
}
}super() is not optional politeness: The parent's constructor is what sets the inherited attributes. Skip super().__init__ and the object exists with those attributes missing — a fault that appears far later, wherever one is first read.
Free preview
This is the free notes preview
You're reading the free notes. Aimnova Pro unlocks the full study experience — and you can try it with your first topic free to keep:
- FlashcardsLock in vocabulary and key terms with spaced repetition.
- Practice questionsAnswer exam-style questions and get instant AI marking.
- Mock exams & past-paper vaultSit full mocks and see exactly how examiners award marks.
- Personalised study planA daily plan built around your exam date and weak areas.
| Inheritance (is-a) | Composition (has-a) | |
|---|---|---|
| Reads as | A SavingsAccount is an Account | A Car has an Engine |
| Child gets | Everything the parent has | Only what it asks the part to do |
| Change the parent | Every child is affected | Only the direct user |
| Use when | The child genuinely is a kind of the parent | One thing is built from another |
What a child class can reach in its parent
| Parent member is | Java: can the child use it directly? | Python |
|---|---|---|
| public | Yes, and so can every other class | Normal names, e.g. self.owner |
| protected | Yes, and so can classes in the same package | By convention one underscore, e.g. self._balance: "for this class and its children" |
| default (no modifier) | Only if the child is in the same package | No equivalent |
| private | No: it is inherited but hidden; the child must use the parent's public or protected methods (e.g. a getter) | Two underscores, e.g. self.__pin, is renamed so a child cannot reach it by that name |
Say the sentence out loud: "A Car is an Engine" is absurd, so inheritance is wrong there. "A SavingsAccount is an Account" is true, so it is right.
Inheriting for convenience — because a class happens to have methods you want — produces a hierarchy nobody can reason about.
What inheritance actually buys
- Shared attributes defined once, so they cannot drift apart
- A change to shared behaviour is one edit, not one per subclass
- Children can be treated as the parent wherever the parent is expected
- New kinds can be added without touching existing code
Learn what examiners really want
See exactly what to write to score full marks. Our AI shows you model answers and the key phrases examiners look for.
Override
A child can redefine a parent method. Its version is used for objects of that class; the parent's is untouched for everything else.
Extend rather than replace
Call the parent's version with super() and add to it, rather than copying its code into the child. Copied code is code that will diverge.
Keep hierarchies shallow
Three levels deep and finding where a method is actually defined becomes a search. Depth is a cost paid on every read of the code.
Protected, not private
A child cannot see the parent's private members. Attributes the child must reach are marked protected instead.
Inheritance is the tightest coupling there is: A child depends on the parent's internals, not just its interface. Changing the parent can break every subclass at once — which is why "prefer composition" is common advice, and why the is-a test matters.
How this is tested — you must justify inheritance with the is-a test AND with what duplication would cost. It comes up two ways:
Paper 2 — working with code
- Explain inheritance and its purpose, 3-4 marks
- Identify the parent class in a scenario
- State what a child inherits
Paper 2 — the algorithmic-thinking question
- Design a hierarchy and justify it
- Explain why inheritance is wrong for a given pair
The classic trap: Naming the parent class and stopping. The marks are for why: the is-a relationship holds, and the shared attributes would otherwise be defined twice and drift apart.
A library system handles Books and DVDs. Both have a title, an ID and a loan status; a Book also has an author and page count, a DVD a duration and certificate. Construct a class design and justify the use of inheritance.
Model answer plan
See the answer broken into clear points, with guidance on how each point earns marks.