Chapter 4 · Mid-level Developer

Your first class: __init__, methods, self

A class is a blueprint for making objects. Each object (an *instance*) carries its own data (attributes) and its own behaviour (methods). It's how you bundle related state and the functions that act on it into one tidy thing.

Three pieces to learn:

  • __init__ — the *constructor*, run automatically when you create an instance. It sets up the starting attributes.
  • self — the first parameter of every method; it refers to *this particular instance*. You store data on it (self.count = 0) and read it back (return self.count). You don't pass self yourself — Python supplies it.
  • methods — functions defined inside the class. Call them on an instance: c.increment().

You make an instance by calling the class like a function: c = Counter(10). That runs __init__ with start=10, and c now has its own count. A second Counter() is completely independent — separate state.

Syntax

class Counter:
    def __init__(self, start=0):   # constructor
        self.count = start          # instance attribute

    def increment(self, amount=1):  # method
        self.count += amount

    def value(self):
        return self.count

c = Counter(10)   # make an instance
c.increment()     # call a method
c.value()         # → 11

Worked examples

Define a class, create an instance, call methods

class Counter:
    def __init__(self, start=0):
        self.count = start

    def increment(self, amount=1):
        self.count += amount

    def decrement(self, amount=1):
        self.count = max(0, self.count - amount)   # floor at 0

    def value(self):
        return self.count

c = Counter(5)
c.increment(3)
print(c.value())   # 8
c.decrement(20)
print(c.value())   # 0  (clamped, not -12)

Each instance has its own state

a = Counter()      # start=0
b = Counter(100)
a.increment()
print(a.value())   # 1
print(b.value())   # 100  (untouched — separate object)

self carries the data between methods

class Greeter:
    def __init__(self, name):
        self.name = name

    def hello(self):
        return f'Hi, {self.name}'

g = Greeter('Sam')
print(g.hello())   # Hi, Sam

Common mistakes & gotchas

Forgetting self in the method signature

Every instance method's first parameter must be selfdef value(): raises TypeError: value() takes 0 positional arguments but 1 was given when called on an instance, because Python passes the instance in automatically.

Forgetting self. when storing or reading

Inside a method, count = 5 makes a throwaway *local* variable that vanishes when the method ends. To persist it on the instance you need self.count = 5. Likewise read it back as self.count, not bare count.

Calling a method without the parentheses

c.value (no brackets) gives you the method *object*, not its result — you'll see something like <bound method ...>. You must call it: c.value().

Why it matters

Classes are how programs grow beyond a handful of functions. When you have data and the operations on it travelling together — a user, a job, a shopping cart, a database connection — a class keeps them coherent. Object-oriented design underpins most large codebases and nearly every framework you'll use.