Modern Class Tooling
Advanced · 18 min read · ▶ live playground · ✦ checkpoint
Some classes are mostly ceremony: store three fields, print nicely, compare by value, or offer one helper that does not need an instance. Python gives you modern class tools for those jobs, so your code says what the class means instead of spelling out every tiny setup step.
Put helpers on the class
A static method is a method stored on a class that receives neither self nor the class itself. Use it for a helper that belongs near the class but does not need object state.
A class method is a method stored on a class that receives the class as its first parameter, usually named cls, so it can build or adjust objects through the class. A decorator is syntax that adjusts a function or method; decorators get their full story in lesson 12.2. For now, read @staticmethod as "do not pass self," and @classmethod as "pass the class as cls."
Think of a class as a house blueprint again. A static method is a note printed on the blueprint. A class method is a front-desk clerk who can build a house from a different kind of order form.
class ScoreCard:
def __init__(self, player, points):
self.player = player
self.points = points
@staticmethod
def is_valid_points(points):
return points >= 0
@classmethod
def from_text(cls, text):
player, points_text = text.split(":")
points = int(points_text)
if not cls.is_valid_points(points):
raise ValueError("points cannot be negative")
return cls(player, points)
def label(self):
return f"{self.player}: {self.points} points"
print(ScoreCard.is_valid_points(80))
card = ScoreCard.from_text("Mina:120")
print(card.label())
print(type(card).__name__)TrueMina: 120 pointsScoreCard
is_valid_points is a static method because it only needs the number. from_text is a class method because it creates a new object through cls(...). That keeps the construction rule attached to the class without forcing callers to split and convert the text themselves.
Use data classes for records
A data class is a class made with @dataclass that automatically writes common methods for storing data. It uses type hints as field labels, then creates an __init__, a useful __repr__, and value-based __eq__ for you.
Boilerplate means repetitive setup code that says little about the idea. Data classes remove a lot of it when your class is mostly a small record.
from dataclasses import dataclass
@dataclass
class QuestResult:
player: str
quest: str
points: int
first = QuestResult("Mina", "Crystal Cave", 120)
second = QuestResult("Mina", "Crystal Cave", 120)
third = QuestResult("Ravi", "Moon Gate", 95)
print(first)
print(first == second)
print(first == third)
print(first.points)QuestResult(player='Mina', quest='Crystal Cave', points=120)TrueFalse120
Without @dataclass, you would write the constructor, representation, and equality method yourself. With it, the class body stays focused on the fields: player, quest, and points.
Use data classes for plain data records. If a class is mostly behavior, validation, or a carefully protected interface, a regular class may still be clearer.
Try it - add one field
Run this, then add a mood: str field to PracticeResult. Update the object creation and print the result again.
Use enums for fixed choices
An enumeration, usually shortened to enum, is a class that names a fixed set of allowed choices. Use it when plain strings would be too easy to mistype.
from enum import Enum
class Mood(Enum):
READY = "ready"
FOCUSED = "focused"
TIRED = "tired"
class PlayerState:
def __init__(self, player, mood):
self.player = player
self.mood = Mood(mood)
def message(self):
return f"{self.player} is {self.mood.value}"
state = PlayerState("Ravi", Mood.FOCUSED)
print(state.mood)
print(state.mood.value)
print(state.message())
print(state.mood == Mood.FOCUSED)Mood.FOCUSEDfocusedRavi is focusedTrue
Mood.FOCUSED is not the string "focused". It is one named member of the Mood enum. The .value attribute gives you the stored string when you need it for display or saving.
Enums make illegal states harder to create. The constructor stores Mood(mood), so callers may pass Mood.FOCUSED or the exact value "focused". The typo "focussed" is not one of the enum values, so Python rejects it during that conversion.
Use __slots__ when shape matters
__slots__ is a class attribute that lists the instance attributes a class is allowed to have. It changes how Python stores instance data, and for many small objects of the same shape, that can reduce per-object memory overhead. It also blocks surprise attributes that are not in the slot list.
class RegularPlayer:
def __init__(self, name, score):
self.name = name
self.score = score
class SlottedPlayer:
__slots__ = ("name", "score")
def __init__(self, name, score):
self.name = name
self.score = score
regular = RegularPlayer("Mina", 120)
regular.mood = "focused"
print(regular.mood)
slotted = SlottedPlayer("Ravi", 95)
try:
slotted.mood = "focused"
except AttributeError as error:
print(f"Rejected: {error}")focusedRejected: 'SlottedPlayer' object has no attribute 'mood' and no __dict__ for setting new attributes
Regular instances usually keep attributes in a per-object dictionary. Slotted instances store only the named slots. That is why slotted.name and slotted.score work, but slotted.mood is rejected.
The pattern is a fixed shelf: every object has the same labeled spaces, and there is no spare drawer for surprise names.
Checkpoint
Answer all three to mark this lesson complete
You now have the tools Python developers reach for when class code starts repeating itself. Next, the course shifts into advanced language features, where iteration, decorators, and context managers get their full treatment.