Unit 11.4A · Term 4

OOP Pillars: Inheritance, Polymorphism & Encapsulation

Inheritance lets a class reuse and extend another class. Polymorphism lets the same method name behave differently across objects. Encapsulation groups data with the methods that protect and control it, improving reliability and reducing accidental misuse.

Learning Objectives

  • 11.4.2.2 Explain the concept of inheritance with examples
  • 11.4.2.1 Explain the concept of polymorphism with examples
  • 11.4.2.x Explain the concept of encapsulation with examples

Conceptual Anchor

The Family Tree Analogy

Inheritance: A child inherits traits from a parent, but can also add new traits or change inherited ones. Polymorphism: Different family members may respond to the same request in different ways, even though the request is the same. Encapsulation: A family may share an address and surname, but private details are kept within the household and accessed through trusted rules.

Rules & Theory

Inheritance

In Cambridge terminology, the base class, parent class, or superclass is the existing class being extended. The derived class, child class, or subclass is the new class that inherits features from it.

Inheritance is used when the relationship is is-a, for example a Car is a Vehicle. The subclass automatically receives accessible attributes and methods from the superclass, and it can add extra features or override inherited methods.

Transport Class Hierarchy Diagram showing 'is-a' relationship
Figure 1: Class hierarchy demonstrating the "is-a" relationship
class Animal: def __init__(self, name): self.name = name def eat(self): print(f"{self.name} is eating.") class Dog(Animal): def bark(self): print("Woof!") d = Dog("Buddy") d.eat() d.bark()

super()

Use super().__init__() inside a child class to call the parent constructor. This is important when the parent class sets up shared state that all subclasses need.

Polymorphism

Polymorphism means one interface can be used with many object types. In Python, the most common form in AS/A Level code is run-time polymorphism through method overriding and duck typing: if an object has the required method, it can be used.

Compile-time polymorphism refers to method overloading in some languages, where multiple methods have the same name but different parameter lists. Python does not support traditional compile-time overloading in the same way; instead, it usually uses default parameters, variable-length arguments, or different method names.

Run-time polymorphism occurs when a subclass provides its own version of a method from the parent class. Python decides which version to run at execution time, based on the actual object.

Polymorphism with geometric shapes
Figure 2: Different objects can share one interface and respond differently
class Cat(Animal): def speak(self): return "Meow" class Dog(Animal): def speak(self): return "Woof" animals = [Cat("Kitty"), Dog("Buddy"), Cat("Luna")] for animal in animals: print(f"{animal.name} says {animal.speak()}")

Encapsulation

Encapsulation means hiding an object's internal data and forcing access through methods. In Python, attributes with no underscore are public, a single underscore such as _balance is a protected convention meaning "internal use", and a double underscore such as __balance triggers name mangling to make direct access less likely.

Name mangling changes __balance inside the class to a form like _BankAccount__balance. This is not true security, but it reduces accidental access and helps enforce a class interface. Good design uses getters and setters, or the @property decorator, to control reading and updating values safely.

class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.__balance = balance @property def balance(self): return self.__balance def deposit(self, amount): if amount > 0: self.__balance += amount def withdraw(self, amount): if 0 < amount <= self.__balance: self.__balance -= amount acc = BankAccount("Amina", 100) acc.deposit(50) acc.withdraw(30) print(acc.balance)

Worked Examples

Video 1: Python OOP – Classes and Objects Explained for Beginners
Video 2: Python Inheritance and Polymorphism Explained

1 Shapes Hierarchy

import math class Shape: def area(self): pass class Rectangle(Shape): def __init__(self, w, h): self.w = w self.h = h def area(self): return self.w * self.h class Circle(Shape): def __init__(self, r): self.r = r def area(self): return math.pi * self.r ** 2 shapes = [Rectangle(10, 5), Circle(7)] for s in shapes: print(f"Area: {s.area():.2f}")

2 Game Characters

class Character: def __init__(self, name): self.name = name self.health = 100 def attack(self): print("Basic attack!") class Warrior(Character): def attack(self): print(f"{self.name} swings a sword!") class Mage(Character): def attack(self): print(f"{self.name} casts a fireball!") hero = Warrior("Arthur") wizard = Mage("Merlin") hero.attack() wizard.attack()

3 Bank Account Encapsulation

class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.__balance = balance def get_balance(self): return self.__balance def deposit(self, amount): if amount > 0: self.__balance += amount def withdraw(self, amount): if 0 <= amount <= self.__balance: self.__balance -= amount acct = BankAccount("Noah", 200) acct.withdraw(50) print(acct.get_balance())

Pitfalls & Common Errors

Overriding Mistakes

If you misspell an overridden method name, Python treats it as a new method. The parent method will still run when the object is used polymorphically.

Forgetting super().__init__

If a child class defines its own __init__, it does not automatically call the parent constructor. You must call it manually or inherited attributes may not exist.

Type Checking vs Polymorphism

Using isinstance() or long if/elif chains before calling methods weakens polymorphism. Prefer a shared interface and let each object decide its own behaviour.

Encapsulation Misconceptions

Do not assume __ makes data truly secure. Name mangling only discourages direct access; it does not encrypt the value. Also avoid reading or changing attributes directly when getter and setter methods are provided, because that bypasses validation and can break class rules.

Graded Tasks

Remember

Describe the purpose of encapsulation. [2 marks]

Understand

State the difference between a superclass and a subclass, using Cambridge terminology. [2 marks]

Apply

Trace the output of a program that creates a parent class and two subclasses that override the same method. Explain why each output line is different. [4 marks]

Create

Design a BankAccount class for a school savings system. Include an encapsulated balance, deposit and withdraw methods, and explain why direct access to the balance should be avoided. [6 marks]