🧱
Coding🎓 Ages 14-18Intermediate 11 min read

Classes and Objects in Python: An Intro to OOP

Learn object-oriented programming in Python: define classes, create objects, use __init__, attributes and methods, with line-by-line code and a worked Dog class example.

Key takeaways

  • A class is a blueprint; an object is a real thing built from that blueprint
  • __init__ runs automatically when you create an object and sets up its attributes
  • self refers to the specific object a method is working on
  • Methods are functions that belong to a class and act on its data

Why objects exist

Imagine writing a game with three characters. Each one has a name, a health value and the ability to attack. Without classes you might create name1, health1, name2, health2, and a pile of separate functions. It quickly becomes a mess.

Object-oriented programming (OOP) offers a cleaner idea: bundle the data (name, health) and the behaviour (attack) together into one unit called an object. A class is the blueprint that describes what every object of that kind looks like and can do.

This builds directly on what you already know about functions and parameters and dictionaries in Python — a class is like a smarter, reusable container for related data and actions.

Defining your first class

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        print(self.name + " says Woof!")

Line by line:

  1. class Dog: — starts the blueprint. By convention class names use CamelCase (capital first letter).
  2. def __init__(self, name, age): — the initialiser. Python runs it automatically whenever a new Dog is made. self is the object being built; name and age are values we pass in.
  3. self.name = name — stores the passed-in name on the object as an attribute. Now this dog remembers its name.
  4. self.age = age — does the same for age.
  5. def bark(self): — a method, which is just a function that belongs to the class. Every method takes self first.
  6. print(self.name + " says Woof!") — uses this object's own name.

Creating and using objects

rex = Dog("Rex", 4)
luna = Dog("Luna", 2)

print(rex.name)   # Rex
print(luna.age)   # 2

rex.bark()        # Rex says Woof!
luna.bark()       # Luna says Woof!

Dog("Rex", 4) calls the class like a function. Behind the scenes Python creates a fresh object, runs __init__ with self set to that new object, and hands it back to you. We store it in rex.

Notice that rex and luna are separate objects. Each has its own name and age. When you call rex.bark(), Python sets self to rex, so it prints Rex's name. The same method behaves differently for each object because self changes.

Worked example: a bank account

Let's model something with behaviour that changes its own data.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance = self.balance + amount
        print(self.owner + " deposited " + str(amount))

    def withdraw(self, amount):
        if amount > self.balance:
            print("Not enough money!")
        else:
            self.balance = self.balance - amount
            print(self.owner + " withdrew " + str(amount))

# Using the class
account = BankAccount("Sam", 100)
account.deposit(50)      # Sam deposited 50
account.withdraw(30)     # Sam withdrew 30
account.withdraw(200)    # Not enough money!
print(account.balance)   # 120

Walk through it slowly:

  • balance=0 gives a default value, so BankAccount("Sam") would start at 0. We passed 100, so it starts at 100.
  • deposit adds to self.balance — it changes the object's own stored data.
  • withdraw first checks whether there is enough money. The method uses an if/else decision, exactly like in conditionals in Python.
  • After two successful operations, the balance is 100 + 50 - 30 = 120.

The key insight: the object remembers its balance between calls. Each method reads and updates self.balance. That memory is what makes objects powerful.

Class vs object, one more time

TermMeaningExample
ClassThe blueprintDog
Object (instance)A real thing built from the classrex
AttributeData on the objectrex.name
MethodAn action the object can dorex.bark()

Common mistakes

  • Forgetting self inside a method definition. def bark(): will raise an error when called as rex.bark() because Python still passes the object in.
  • Forgetting self. when storing data. Writing name = name inside __init__ creates a temporary local variable that vanishes; you need self.name = name.
  • Confusing the class with an object. Dog.bark() on its own fails because there is no specific dog for self to be.

Try it yourself

  1. Add a birthday method to Dog that increases self.age by 1 and prints "Happy birthday!".
  2. Create a Rectangle class with width and height attributes and an area method that returns self.width * self.height.
  3. Give BankAccount a transfer method that withdraws from one account and deposits into another. Hint: it can take a second account object as a parameter.

Once you are comfortable here, explore how objects store collections of data in Python nested lists and dictionaries, and practise the logic skills you used in withdraw with Python comparison and logic.

Quick quiz

Test yourself and earn XP

What is a class in Python?

When does the __init__ method run?

What does self refer to inside a method?

Given dog = Dog('Rex'), how do you call its bark() method?

What is an attribute?

FAQ

A class is the blueprint (for example, the idea of a Dog), and an object is a specific thing built from it (for example, a dog named Rex). One class can create many objects.

self lets a method know which object it is working on. You do not pass it yourself; Python fills it in automatically when you call object.method().

No. Many small programs use only functions and variables. Classes become useful when you have data and behaviour that naturally belong together, like a game character with health, position and an attack action.