First exams 2027 · HL only

B3.2.3 · Fundamentals of OOP for multiple classes (HL)

Abstraction and abstract classes

Writing down what every subclass must be able to do, without pretending to know how any of them will do it.

HL only Typically 3–6 marks · AO1/AO2 Command term: explain

Explainer

Lansdown Vehicle Hire rents out cars, vans, motorcycles and trucks. Every booking screen has to show two things: what it costs per day, and what category of licence the driver needs to be allowed to drive it. Neither has a generic answer. A motorcycle is category A, a car or van is B, a truck is C, and the daily rates are nowhere near each other. Inventing a default would not merely be untidy: it would quote the wrong price, or hand a truck to someone licensed only for a car.

Df

Abstraction is the practice of exposing only the essential features of something while keeping the details of how it works out of sight. In OOP it means designing around what an object can be asked to do, rather than around how any particular object does it.

Df

An abstract class is a class that cannot be instantiated on its own. It exists to be inherited from. It may declare abstract methods, which have a name and a signature but no body, and which every concrete subclass is obliged to implement.

Together these give the hire firm exactly what it needs: a written guarantee that every vehicle type, including ones nobody has added yet, can answer "what do you cost?" and "what licence do you need?", without the booking screen knowing a single thing about cars, motorcycles or trucks.

Explainer

The test for when an abstract class is the right choice

Not every parent class should be abstract. The deciding question is short, and it is the one examiners reward:

The test

Every subclass must have this method, and there is no sensible default implementation you could write for it. If you can write a reasonable default, use a normal class and let subclasses override it. If any default you could write would be a guess, make the method abstract and force each subclass to supply its own.

Applied at Lansdown: licence_category() has no defensible default, so it is abstract. hire_summary() is the same sentence for every vehicle, so it is written once as a normal method and inherited. One abstract class can hold both kinds at once, which is a point worth stating explicitly in an answer.

Worked example

The contract, in code

abstract class Vehicle
Abstract
daily_rate()

Declared, never implemented here. Every concrete subclass must supply its own, because a rate that suits a motorcycle would badly undercharge a truck.

Abstract
licence_category()

Same reasoning, with a legal edge: guessing this wrong hands a vehicle to a driver who is not entitled to drive it.

Concrete
hire_summary()

Written once and inherited by all. It calls the two methods above without knowing or caring which subclass will answer.

?

What is from abc import ABC, abstractmethod? Python has no abstract keyword, so unlike Java it cannot mark a class abstract in the language itself. The tools come from a module in the standard library instead, called abc, short for Abstract Base Class. Nothing needs installing, it ships with Python.

The line brings in two separate things. ABC is a class to inherit from, and inheriting it is what makes your own class abstract. abstractmethod is a decorator, written on the line above a method, marking that method as one every concrete subclass is obliged to implement. You need both: ABC alone with no abstract methods would still be perfectly instantiable.

from abc import ABC, abstractmethod

class Vehicle(ABC):                      # inheriting ABC makes it abstract
    def __init__(self, reg, model):
        self.reg = reg
        self.model = model

    @abstractmethod
    def daily_rate(self):                # no body: subclasses MUST write it
        pass

    @abstractmethod
    def licence_category(self):
        pass

    def hire_summary(self):              # CONCRETE: shared by every vehicle
        return f"{self.reg} {self.model}: {self.daily_rate()} per day, licence {self.licence_category()}"


class Car(Vehicle):
    def daily_rate(self):
        return 45

    def licence_category(self):
        return "B"
abstract class Vehicle {                // the abstract keyword does it here
    protected String reg;
    protected String model;

    public abstract int dailyRate();          // no body, ends with a semicolon
    public abstract String licenceCategory();

    public String hireSummary() {           // CONCRETE: shared by every vehicle
        return reg + " " + model + ": " + dailyRate() + " per day";
    }
}

class Car extends Vehicle {
    @Override
    public int dailyRate() {
        return 45;
    }

    @Override
    public String licenceCategory() {
        return "B";
    }
}

Python has no abstract keyword. It uses the standard library instead: inherit from ABC and mark the required methods with @abstractmethod. Java has the keyword built into the language, and an abstract method is written with a semicolon where the body would go.

Worked example

The two things an abstract class refuses

This is what makes the contract real rather than a comment. Both of these are errors, caught before any wrong lorry is loaded.

1. You cannot create one directly

# Vehicle is a description, not a thing you can hire
v = Vehicle("LV01 XYZ", "generic")

TypeError: Can't instantiate abstract class Vehicle with abstract methods daily_rate, licence_category

2. You cannot half-implement it

# forgets licence_category()
class Van(Vehicle):
    def daily_rate(self):
        return 65

v = Van("LV22 KLM", "Transit")

TypeError: Can't instantiate abstract class Van with abstract method licence_category

Notice when the second error appears: not when the incomplete class is defined, but the moment something tries to create one. The contract is checked at instantiation, which is still long before that van could reach a booking screen.

Explainer

Why this makes code more modular

The first elaboration bullet asks specifically about modular code fragments, so it is worth being precise about what changes.

# the booking screen: it knows nothing about cars, motorcycles or trucks
def show_fleet(vehicles):
    for v in vehicles:
        print(v.hire_summary())

This function depends on the abstract class alone, never on any concrete vehicle type. That has three consequences worth naming in an answer:

1

The fragments can be written separately. One developer builds the booking screen while another writes Truck. Neither needs the other's code to exist, only the agreed contract.

2

They can be changed independently. Changing how a truck works out its daily rate cannot break the booking screen, because the screen only ever relied on the method existing and returning a value.

3

A missing piece fails loudly and early. Without the abstract class, a vehicle that forgot licence_category() would only be discovered when a customer reached the checkout. With it, the object cannot be created at all.

Explainer

Abstract class, or an ordinary parent class?

Both give inheritance and shared code. Only one enforces anything.

Ordinary parent class

Real example: a Vehicle whose licence_category() returns "B" as a fallback, because most of the fleet is cars and vans.

Advantage

  • Simpler, and usable on its own (a plain Vehicle object can be created for quick testing, with no subclass required)

Disadvantage

  • A forgotten override fails silently (a new truck class that never defines licence_category() quietly inherits "B", so the system lets a car driver book a truck and nothing in the code complains)
Abstract class

Real example: the Vehicle(ABC) above, where licence_category() has no body at all.

Advantage

  • The contract is enforced by the language (a subclass that forgets a required method cannot be instantiated, so the mistake surfaces immediately instead of becoming a wrongly licensed hire)

Disadvantage

  • More rigid, and never usable alone (every subclass must implement every abstract method even when one of them has nothing meaningful to say, and the class itself can never be instantiated for testing)

Try it yourself

Try to break the contract

The Lansdown classes, live and editable. Two experiments: uncomment the line that tries to build a bare Vehicle, and the Van class that deliberately forgets licence_category(). Both should be refused.

main.py
Loading Python engine…
Output
Press Run to see the output here.

Running in offline fallback mode. The full Python engine couldn’t load (blocked school network, or no internet), so this console switched automatically to a small hand-written engine. It runs this page’s scenario, including ABC, @abstractmethod and the refusal to instantiate an incomplete class, but it isn’t full Python.

Check your understanding

Vehicle is abstract with one abstract method and one concrete method. Car extends it and implements the abstract method. Which statement is correct?

Common mistake

Markers reported this pattern for B3.2.3

RecurringCandidates claim an abstract class cannot contain any working code, describing it as a list of empty method names. An abstract class may hold concrete methods, attributes and a constructor; what it cannot do is be instantiated. Answers built on the stricter, wrong version lose marks even where the surrounding reasoning is good.

The second error is choosing abstraction where plain inheritance would do. If a sensible default implementation exists, an ordinary parent class is the better design, and saying so demonstrates the judgment the command term is asking for.

Answer frame that scores: state that it cannot be instantiated → state what subclasses are obliged to provide → note that it may still supply shared concrete code.

✕

What a weak answer looks like: “An abstract class is an empty class containing only method names, which the subclasses then fill in.” The first half is wrong: hire_summary() is real, working, inherited code sitting inside an abstract class.

✓

What it should say: “Vehicle cannot be instantiated. It declares licence_category() as abstract, so every concrete subclass must implement it, while still providing hire_summary() as working code that all vehicles inherit unchanged.” Instantiation, obligation, and shared code all stated.

Revision questions

Past-paper style. Write an answer, then reveal the marking points and mark yourself.

Question 1 · 2 marks

[2]Explain why licence_category() is declared abstract in Vehicle, while hire_summary() is not.

Question 2 · 4 marks

[4]Lansdown adds minibuses at 80 per day, requiring a category D1 licence. Construct Minibus so that it satisfies the Vehicle contract.

Question 3 · 3 marks

[3]A developer proposes replacing the abstract class with an ordinary parent class whose licence_category() returns "B". Evaluate this proposal for the hire firm.

Flashcards

Active recall for this statement’s key terms.