bigonotes
CS ConceptsOOP

OOP Principles Recap

A one-page Python recap of the four OOP pillars plus abstract base classes vs protocols, tying the OOP lessons together.

4 min readLast updated OOP

Object-oriented programming organizes code into objects that bundle data (what an object has) with behavior (what it does). This recap pulls the four pillars together in Python, then covers the one idea the earlier lessons only touched: interfaces. Each pillar has its own deeper lesson linked below.

Classes and objects#

A class is the blueprint; an object is an instance made from it.

class Watch:
    def __init__(self, brand: str, hour: int, minute: int):
        self.brand = brand  # attributes: what it has
        self.hour = hour
        self.minute = minute

    def show_time(self) -> None:  # behavior: what it does
        print(f"Time is {self.hour}:{self.minute:02d}")


my_watch = Watch("Casio", 10, 30)
my_watch.show_time()  # Time is 10:30

The four pillars#

A handy mnemonic is PIE + A (or "A PIE"): Abstraction, Polymorphism, Inheritance, Encapsulation.

Encapsulation#

Hide internal state and control access to it. Like a phone's circuitry sealed in a case, you only use the buttons. Deep dive: Encapsulation.

class Watch:
    def __init__(self):
        self._hour = 0  # internal: change it through set_time
        self._minute = 0

    def set_time(self, h: int, m: int) -> None:
        # Validation lives with the data, so no caller can set 25:99
        if 0 <= h <= 23 and 0 <= m <= 59:
            self._hour, self._minute = h, m
        else:
            print("Invalid time!")


Watch().set_time(25, 99)  # Invalid time!

Inheritance#

Build new classes from existing ones by naming a parent class in the class statement. The child gets the parent's members plus its own. Deep dive: Inheritance.

class Vehicle:
    def move(self) -> None:
        print("Vehicle is moving")


class Car(Vehicle):
    def honk(self) -> None:
        print("Beep beep!")


# A Car can move() (inherited) and honk() (its own),
# but it can't use a sibling class's methods.
car = Car()
car.move()
car.honk()

Polymorphism#

"Many forms": the same call behaves differently per object. In Python this is overriding a parent's method, plus duck typing: any object with the right method works. Deep dive: Polymorphism.

class Animal:
    def make_sound(self) -> None:
        print("Some sound")


class Dog(Animal):
    def make_sound(self) -> None:
        print("Woof!")


class Cat(Animal):
    def make_sound(self) -> None:
        print("Meow!")


for a in [Dog(), Cat()]:
    a.make_sound()  # Woof! then Meow!: decided by the actual object

Abstraction#

Expose what an object does, hide how. An abstract base class declares methods without bodies that subclasses must implement, and you can't instantiate it. Deep dive: Abstraction.

import math
from abc import ABC, abstractmethod


class Shape(ABC):
    @abstractmethod
    def calculate_area(self) -> float:  # subclasses must provide it
        ...

    def display(self) -> None:          # concrete, shared
        print("This is a shape")


class Circle(Shape):
    def __init__(self, radius: float):
        self._radius = radius

    def calculate_area(self) -> float:
        return math.pi * self._radius ** 2


print(round(Circle(1).calculate_area(), 2))  # 3.14
# Shape() raises TypeError: can't instantiate an abstract class

Interfaces#

An interface is a contract of abilities: a list of methods a class promises to provide. Python has no interface keyword; you get the same thing two ways:

  • An ABC with only abstract methods: classes inherit from it explicitly, and Python refuses to instantiate a subclass that misses a method.
  • A typing.Protocol: any class with matching methods satisfies it, without inheriting from it (structural typing, checked by type checkers).
  • A class can inherit from several ABCs, so it can promise many abilities.
from typing import Final, Protocol


class Playable(Protocol):
    def play(self) -> None: ...
    def pause(self) -> None: ...
    def stop(self) -> None: ...


MAX_VOLUME: Final = 100  # constants live at module or class level


class MusicPlayer:  # no inheritance needed: it just has the methods
    def play(self) -> None:
        print("Playing")

    def pause(self) -> None:
        print("Paused")

    def stop(self) -> None:
        print("Stopped")


def start(device: Playable) -> None:
    device.play()


start(MusicPlayer())  # Playing

Abstract base class vs protocol#

Both describe a contract; they differ in how a class signs up for it.

Abstract base class (ABC) Protocol
Mix of abstract and concrete methods Just method signatures
Can have instance state and __init__ No state of its own
Classes must inherit from it Any class with the right methods fits
"Partly built, finish me" "Here are the abilities you must have"
Use for closely related classes sharing code Use for a capability unrelated classes share

All four together#

One small hierarchy shows every pillar at once:

from abc import ABC, abstractmethod


# Abstraction: an incomplete template
class Employee(ABC):
    def __init__(self, name: str):
        self._name = name  # Encapsulation: internal state...

    @property
    def name(self) -> str:  # ...exposed through a read-only accessor
        return self._name

    @abstractmethod
    def calculate_salary(self) -> float:
        ...


# Inheritance: Manager is an Employee
class Manager(Employee):
    def __init__(self, name: str, base_salary: float, bonus: float):
        super().__init__(name)
        self._base_salary = base_salary
        self._bonus = bonus

    # Polymorphism: each Employee type computes salary its own way
    def calculate_salary(self) -> float:
        return self._base_salary + self._bonus


m = Manager("Ana", 100_000, 20_000)
print(m.name, m.calculate_salary())  # Ana 120000

Key terms#

These are the words you'll hear in every OOP discussion:

Term Meaning
Class / object Blueprint / instance
Attribute (field, property) What an object has
Method (behavior) What an object does
Encapsulation Hiding and protecting data
Inheritance Child classes reuse parent features
Polymorphism Same call, different behavior
Abstraction Hiding complexity behind a contract
Interface A contract of required methods (ABC or Protocol)
Override Replacing a parent's method in a child

Key takeaways#

  • Objects bundle state and behavior; classes are their blueprints.
  • Encapsulation protects data, inheritance reuses code, polymorphism varies behavior, abstraction hides complexity.
  • Abstract base classes share code among related types; protocols define capabilities any class can adopt.
  • A Python class can inherit from several bases, so it can sign up for many interfaces.

Found a mistake? Tell me on LinkedIn ↗

Further reading#