⚡IntermediateObject-Oriented Python

Classes and Objects

Defining classes, creating objects, and using instance variables and methods.

Overview

Object-Oriented Programming (OOP) organises code around objects bundles of data (attributes) and behaviour (methods). A class is a blueprint; an object (instance) is a concrete thing created from that blueprint. In Python every class is defined with the `class` keyword. The special method `__init__` (the constructor) runs automatically when you create a new instance and is where you set up instance variables using `self`. `self` is a reference to the current instance. Every instance method receives it as its first parameter Python passes it automatically when you call a method on an object. You can name it anything, but `self` is the universal convention. Class variables are shared across all instances; instance variables (set via `self`) are unique to each object. Understanding this distinction prevents subtle bugs where all instances accidentally share mutable state.

Key Points

  • •Define a class with `class Name:`; the constructor is `def __init__(self, ...):`
  • •`self` refers to the current instance always the first parameter of instance methods
  • •Instance variables: `self.name = value` inside `__init__` unique per object
  • •Class variables: defined directly under the class shared by all instances
  • •Create an instance: `obj = MyClass(args)` Python calls `__init__` automatically
  • •Access attributes: `obj.name`; call methods: `obj.method()`
  • •`__str__` controls what `print(obj)` shows always define it for readable output

Examples

Defining a class and creating instances

class Dog:
    species = "Canis familiaris"   # class variable shared by all dogs

    def __init__(self, name, age):
        self.name = name           # instance variable
        self.age = age

    def bark(self):
        return f"{self.name} says: Woof!"

    def __str__(self):
        return f"Dog(name={self.name}, age={self.age})"

rex = Dog("Rex", 3)
buddy = Dog("Buddy", 5)

print(rex.bark())           # Rex says: Woof!
print(buddy.name)           # Buddy
print(Dog.species)          # Canis familiaris
print(rex)                  # Dog(name=Rex, age=3)

Class variables vs instance variables

class Counter:
    count = 0    # class variable shared state

    def __init__(self, name):
        Counter.count += 1
        self.name = name
        self.id = Counter.count   # unique per instance

a = Counter("first")
b = Counter("second")
c = Counter("third")

print(Counter.count)   # 3 all instances see this
print(a.id, b.id, c.id)  # 1 2 3

Use class variables for state shared across all instances, instance variables for per-object state.

Methods that modify state

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

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self.balance += amount

    def withdraw(self, amount):
        if amount > self.balance:
            raise ValueError("Insufficient funds")
        self.balance -= amount

    def __str__(self):
        return f"{self.owner}: ${self.balance:.2f}"

acc = BankAccount("Alice", 100)
acc.deposit(50)
acc.withdraw(30)
print(acc)   # Alice: $120.00

Common Mistakes

  • Forgetting `self` as the first parameter of instance methods causes TypeError when calling
  • Using a mutable class variable (like a list) instead of setting it in __init__ all instances share the same list
  • Calling `obj.__init__()` manually just create a new instance instead

Note

Always define `__str__` for your classes. `print(obj)` will show something useful instead of `<__main__.Dog object at 0x...>`, which makes debugging far easier.

Exercises

Write Python code to solve each exercise. Run it in your browser and check your output.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What is the purpose of `__init__` in a Python class?