OOP in Python
When a program models something from the real world — a bank account, a user, a car — that entity usually has both data (balance, name, speed) and actions (deposit money, introduce itself, accelerate). Object-oriented programming (OOP) lets you pack data and actions together into one entity — an object, described by a template — a class.
First example
Let's describe a simple Car class:
Python 3.13class Car: def __init__(self, make, model): self.make = make # make self.model = model # model self.is_running = False # whether the engine is on def start_engine(self): """Start the engine""" if not self.is_running: self.is_running = True return f"{self.make} {self.model}: Engine started" return f"{self.make} {self.model}: Engine was already started" def stop_engine(self): """Stop the engine""" if self.is_running: self.is_running = False return f"{self.make} {self.model}: Engine stopped" return f"{self.make} {self.model}: Engine was already stopped" # Create an instance of the Car class my_car = Car("Toyota", "Corolla") print(my_car.start_engine())Toyota Corolla: Engine startedprint(my_car.stop_engine())Toyota Corolla: Engine stopped# We can create another instance of Car another_car = Car("Honda", "Civic") print(another_car.start_engine())Honda Civic: Engine started
Class vs object
Car is the class: a template that describes what fields and methods any car will have. my_car and another_car are objects (instances of the class) with concrete values for those fields. From one class you can create as many objects as you want, each with its own data.

Terminology
Tied to the example above:
- Car: the class (blueprint)
- my_car, another_car: instances or objects of this class
- make, model, is_running: attributes (the object's data)
- start_engine(), stop_engine(): methods (the object's actions)
- __init__: the special constructor method, called automatically when you create an object via Car(...)
- self: a reference to the object itself; methods use it to access their attributes
The four principles of OOP
OOP is traditionally built on four concepts. Each gets a dedicated lesson later; here just a brief overview:
- Encapsulation: packing data and the methods that work with that data into a single object. External code doesn't poke at implementation details, it works through the class's interface.
- Inheritance: creating a new class on top of an existing one, reusing its attributes and methods.
- Polymorphism: the ability to work with objects of different classes through a unified interface (calling .area() the same way on a circle, square, and triangle).
- Abstraction: highlighting the essential characteristics of an object and hiding the irrelevant details.
Practical example: a bank account
Let's build a richer class that has validation and state:
Python 3.13class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def deposit(self, amount): if amount > 0: self.balance += amount return f"Deposited {amount}. New balance: {self.balance}" return "The deposit amount must be positive" def withdraw(self, amount): if amount > 0: if amount <= self.balance: self.balance -= amount return f"Withdrew {amount}. New balance: {self.balance}" return "Insufficient funds" return "The withdrawal amount must be positive" def get_balance(self): return f"{self.owner}'s balance: {self.balance}" account = BankAccount("John Smith", 1000) print(account.get_balance())John Smith's balance: 1000print(account.deposit(500))Deposited 500. New balance: 1500print(account.withdraw(200))Withdrew 200. New balance: 1300print(account.withdraw(2000))Insufficient funds
The account object holds the data (owner, balance) and provides actions (deposit, withdraw, get_balance). The validation logic (positive amount, sufficient funds) lives inside the methods, right next to the data it acts on. That's encapsulation in action.
Understanding check
Which are the core principles of OOP?
In the next lessons we'll go through each of the four principles in detail with examples and pitfalls. Starting with encapsulation.
