Chapters
Python114 chapters

Classes and objectsChapter 60 of 114

The __init__ Method

Set up each new object with the data it needs.

Running code when an object is made

__init__ runs automatically every time you build an object. Its job is to give the new object its starting data:

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

rex = Dog("Rex", 3)
print(rex.name, rex.age)

Output

Rex 3

The arguments to Dog(...) go to __init__ after self, which Python fills in with the new object.

Every object gets its own

This is the fix for the shared mutable class attribute:

Python
class Dog:
    def __init__(self, name):
        self.name = name
        self.tricks = []

rex = Dog("Rex")
fido = Dog("Fido")
rex.tricks.append("sit")

print(rex.tricks)
print(fido.tricks)

Output

['sit']
[]

Each call to __init__ builds a fresh list, so the dogs no longer share one.

It is not a constructor

__init__ does not create the object — that has already happened by the time it runs — and it must not return anything:

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

print(Dog("Rex").name)

Output

Rex

Returning a value from __init__ raises TypeError. It is an initialiser, and the name says so.

Defaults and validation

__init__ is an ordinary function, so defaults and checks work as usual. Doing the validation here means an invalid object can never exist:

Python
class Dog:
    def __init__(self, name, age=0):
        if not name:
            raise ValueError("a dog needs a name")
        self.name = name
        self.age = age

print(Dog("Rex").age)

try:
    Dog("")
except ValueError as problem:
    print("ValueError:", problem)

Output

0
ValueError: a dog needs a name

Remember the mutable default rule applies here too:

Python
class Dog:
    def __init__(self, name, tricks=None):
        self.name = name
        self.tricks = tricks if tricks is not None else []

print(Dog("Rex").tricks)
print(Dog("Fido", ["roll"]).tricks)

Output

[]
['roll']

Computed attributes

Anything you can work out once, work out here:

Python
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height

r = Rectangle(3, 4)
print(r.area)

Output

12

dataclasses write it for you

When a class is mostly a bundle of fields, @dataclass generates __init__, __repr__ and __eq__:

Python
from dataclasses import dataclass, field

@dataclass
class Dog:
    name: str
    age: int = 0
    tricks: list = field(default_factory=list)

rex = Dog("Rex", 3)
print(rex)
print(rex == Dog("Rex", 3))
print(Dog("Fido").tricks)

Output

Dog(name='Rex', age=3, tricks=[])
True
[]

Note field(default_factory=list): the same mutable-default problem, solved explicitly. A plain tricks: list = [] is rejected outright by dataclasses, which is a rare case of a language stopping you making that mistake.

Test yourself

2 questions

When does __init__ run?

Show the answer

Every time you build an object — It initialises an object that already exists, which is why it must not return anything.

Why does a dataclass need field(default_factory=list) rather than = []?

Show the answer

A single list would be shared by every instance — Dataclasses reject a plain mutable default outright, which is a rare case of the language stopping the mistake.

Next chapter

The __str__ Method

Decide what your object looks like when it is printed.