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:
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:
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:
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:
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:
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:
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__:
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 questionsWhen 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.
The __str__ Method
Decide what your object looks like when it is printed.