Classes and dataclasses
Bundle data with the behaviour that operates on it.
- Define a class with __init__ and methods
- Explain instances, attributes, and self
- Use @dataclass to cut boilerplate for data-holding classes
A class bundles data together with the functions that operate on it. It's a template; each instance is a concrete thing made from it. Classes are how you model the "nouns" of a system — and a clean class is just good decomposition with state attached.
Defining a class
__init__ runs when you create an instance and sets up its data; methods are
functions that act on self (the instance):
class Counter:
def __init__(self, start=0):
self.count = start # an attribute on this instance
def increment(self):
self.count += 1
def value(self):
return self.count
c = Counter() # make an instance
c.increment()
c.value() # 1self is the instance the method was called on; c.increment() passes c as
self automatically. Each instance has its own count — two counters don't
interfere.
dataclasses: classes that mostly hold data
Many classes just hold a few fields. A dataclass generates the boilerplate
(__init__, a readable repr, equality) for you:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
p = Point(3, 4)
p # Point(x=3, y=4) — readable automatically
p == Point(3, 4) # True — value equality, for freeCompare that to writing __init__ and __eq__ by hand. Reach for @dataclass
whenever a class is primarily a typed bundle of fields.
Don't reach for a class by default. If something is just data, a dict or tuple may be enough; if it's just behaviour, a function is enough. Use a class when data and behaviour genuinely belong together.
Where to go next
Next: context managers — guaranteeing setup and cleanup with with.