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.13
class 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 started
print(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.

The Car class as a template with fields make, model, is_running and methods start_engine, stop_engine. Two objects are created from this class: my_car (Toyota Corolla, running=True) and another_car (Honda Civic, running=False)

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:

  1. 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.
  2. Inheritance: creating a new class on top of an existing one, reusing its attributes and methods.
  3. 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).
  4. 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.13
class 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: 1000
print(account.deposit(500))
Deposited 500. New balance: 1500
print(account.withdraw(200))
Withdrew 200. New balance: 1300
print(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.