post_init Method¶
The __post_init__() method is called after the generated __init__() completes, allowing you to perform additional initialization or validation.
Basic post_init Usage¶
from dataclasses import dataclass
from datetime import datetime, timedelta
@dataclass
class Event:
name: str
date: str
duration_minutes: int
def __post_init__(self):
# Convert string to datetime
self.date = datetime.strptime(self.date, "%Y-%m-%d")
# Calculate end time
self.end_time = self.date + timedelta(minutes=self.duration_minutes)
event = Event("Meeting", "2024-02-15", 60)
print(f"{event.name} on {event.date.date()} until {event.end_time.time()}")
Validation in post_init¶
from dataclasses import dataclass
@dataclass
class Age:
years: int
def __post_init__(self):
if self.years < 0 or self.years > 150:
raise ValueError(f"Invalid age: {self.years}")
try:
invalid = Age(-5)
except ValueError as e:
print(f"Error: {e}")
valid = Age(25)
print(f"Valid age: {valid.years}")
Derived Fields¶
from dataclasses import dataclass, field
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False)
perimeter: float = field(init=False)
def __post_init__(self):
# Calculate derived fields
self.area = self.width * self.height
self.perimeter = 2 * (self.width + self.height)
rect = Rectangle(5, 10)
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}") # 50, 30
Complex Initialization Logic¶
from dataclasses import dataclass
from pathlib import Path
@dataclass
class FileConfig:
path: str
mode: str = "r"
encoding: str = "utf-8"
_file = None
def __post_init__(self):
# Validate path exists for read mode
if self.mode == "r" and not Path(self.path).exists():
raise FileNotFoundError(f"{self.path} not found")
# Store as Path object
self.path = Path(self.path)
print(f"Initialized config for {self.path}")
try:
config = FileConfig("/nonexistent/file.txt")
except FileNotFoundError as e:
print(f"Error: {e}")
post_init with Field Modifications¶
from dataclasses import dataclass, field
@dataclass
class Container:
items: list = field(default_factory=list)
def __post_init__(self):
# Ensure items is always a list
if not isinstance(self.items, list):
self.items = list(self.items)
# Sort items
self.items.sort()
container = Container((3, 1, 2))
print(container.items) # [1, 2, 3]
Performance Notes¶
__post_init__()adds overhead to object creation- Keep it lightweight for frequently created objects
- Use for validation, derived fields, and conversions
- Alternative: Use custom
__init__if complex logic needed