Classes & Objects

Difficulty: Intermediate

A class in Python is a blueprint for creating objects. It bundles data (attributes) and functionality (methods) into a single unit. You define a class with the class keyword, and by convention class names use PascalCase. Once a class is defined you create instances of it by calling the class name like a function, which triggers the special __init__ method.

The __init__ method is Python's constructor. It runs automatically every time you create a new instance and is the standard place to initialize the object's attributes. The first parameter of __init__ (and every other instance method) is self, which is a reference to the instance being created. Through self you attach data to the specific object so that each instance carries its own state.

Instance attributes are variables that belong to a particular object. They are typically defined inside __init__ using self.attribute_name = value. Because each instance has its own namespace, two objects created from the same class can hold completely different data in their attributes. This per-instance isolation is fundamental to object-oriented design.

Methods are functions defined inside a class body. Regular instance methods always take self as their first parameter, giving them access to the instance's attributes and other methods. You call a method on an object with dot notation: obj.method(). Python automatically passes the object as the self argument, so you never supply it explicitly when calling.

Beyond instance attributes, Python also supports class attributes, which are shared across all instances. A class attribute is defined directly in the class body (outside any method). When you read an attribute, Python first checks the instance namespace and falls back to the class namespace. Writing to an attribute via self always creates or updates an instance attribute, leaving the class attribute untouched for other instances.

Code examples

Defining a basic class with __init__

class Dog:
    species = 'Canis familiaris'  # class attribute

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

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

    def describe(self):
        return f'{self.name} is {self.age} years old'

dog1 = Dog('Rex', 3)
dog2 = Dog('Buddy', 5)

print(dog1.bark())
print(dog2.describe())
print(dog1.species)
print(dog2.species)

Dog is a class with one class attribute (species) shared by all dogs and two instance attributes (name, age) unique to each dog. Methods bark() and describe() use self to access the specific instance's data.

Instance vs class attribute behavior

class Counter:
    count = 0  # class attribute

    def __init__(self, label):
        self.label = label
        Counter.count += 1  # modify class attribute via class name

    def display(self):
        return f'{self.label}: total instances = {Counter.count}'

c1 = Counter('A')
c2 = Counter('B')
c3 = Counter('C')

print(c1.display())
print(c3.display())
print(Counter.count)

Counter.count is a class attribute modified via the class name itself. Every instance sees the same value because they all share the class namespace. Each instance has its own label attribute set through self.

Multiple methods interacting

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

    def deposit(self, amount):
        self.balance += amount
        return self.balance

    def withdraw(self, amount):
        if amount > self.balance:
            return 'Insufficient funds'
        self.balance -= amount
        return self.balance

    def summary(self):
        return f'{self.owner}: ${self.balance}'

account = BankAccount('Alice', 100)
print(account.summary())
print(account.deposit(50))
print(account.withdraw(30))
print(account.withdraw(200))
print(account.summary())

BankAccount shows methods that modify instance state. deposit and withdraw update self.balance and return the new balance or an error message. The default parameter balance=0 lets you create an account without specifying an initial balance.

Key points

Concepts covered

class, __init__, self, instance attributes, methods, object instantiation