Validate constructor input with __post_init__
Also published in our Blogger archive.
A generated dataclass constructor assigns declared fields before calling __post_init__. That makes __post_init__ a focused place to validate relationships and value constraints that must hold for every constructed Booking. Here, a guest name must contain a non-whitespace character and nights must be at least one. A valid input, Booking("Ada", 2), produces a usable object; invalid inputs produce deliberate ValueError messages.
The example catches both failures only to make the output deterministic. In application code, callers would normally handle the exception at an appropriate boundary rather than continue with an invalid booking. Type annotations document the intended types, but dataclasses do not enforce annotations at runtime. If guest could arrive as a non-string value, validation must explicitly define how to reject or convert it before calling .strip().
__post_init__ runs automatically only when the dataclass-generated __init__ exists. It is also a suitable place to compute values derived from initialized fields, but avoid side effects that make ordinary construction unexpectedly perform I/O. The dataclasses module has been in the standard library since Python 3.7.
AI assistance disclosure: this article was drafted with AI assistance and should be reviewed in the context of its target Python version.
Python dataclasses documentation specifies when __post_init__ is called by a generated initializer.
from dataclasses import dataclass
@dataclass
class Booking:
guest: str
nights: int
def __post_init__(self) -> None:
if not self.guest.strip():
raise ValueError("guest must not be blank")
if self.nights < 1:
raise ValueError("nights must be positive")
booking = Booking("Ada", 2)
try:
Booking(" ", 2)
except ValueError as error:
blank_error = str(error)
try:
Booking("Ada", 0)
except ValueError as error:
nights_error = str(error)
assert (booking.guest, booking.nights) == ("Ada", 2)
assert blank_error == "guest must not be blank"
assert nights_error == "nights must be positive"
print(f"{booking.guest}:{booking.nights}|{blank_error}|{nights_error}")
Ada:2|guest must not be blank|nights must be positive