post_init 是 dataclass 初始化后自动调用的校验与衍生计算方法,用于字段验证、联合校验及 init=False 字段赋值,须只读校验、避免覆盖用户输入,错误时抛 ValueError 并附清晰信息。
在 @dataclass 中,__post_init__ 是初始化后自动调用的方法,适合做字段的二次验证或衍生字段计算。它不能修改 __init__ 的参数签名,但可以访问所有已赋值的字段(包括默认值、field(default_factory=...) 初始化后的结果)。
__post_init__ 执行时,所有字段已完成赋值(含默认值和工厂函数返回值),因此可安全读取并校验。注意:不要在 __post_init__ 里给未声明为 init=False 的字段重新赋值——这会覆盖用户传入的值,且可能破坏预期行为。
self.age > 0、len(self.name) > 2
self.name = self.name.strip()(除非你明确想归一化且接受副作用)field(init=False),它不会出现在 __init__ 参数中,必须在 __post_init__ 中手动赋值验证失败时建议抛出 ValueError(标准做法),或自定义异常类以便上层捕获区分。消息中应包含字段名和具体原因,便于调试。
例如:
def __post_init__(self):
if self.pric
e < 0:
raise ValueError(f"price must be non-negative, got {self.price}")
if not self.title or len(self.title.strip()) == 0:
raise ValueError("title cannot be empty or whitespace-only")
__post_init__ 是唯一能同时访问所有字段的地方,适合做跨字段逻辑检查,比如“结束时间不能早于开始时间”、“密码和确认密码必须一致”等。
self.start_time 和 self.end_time 并比较self.email 和 self.is_verified 做业务规则判断self._validate_date_range()),提升可读性如果字段是通过 default_factory 构造的(如 list、dict),注意它是可变对象,直接修改(如 self.items.append(...))会影响实例状态,但不属于“验证”,而是副作用。验证应只读不改。
若需派生只读字段(如 full_name),推荐用 property;若必须缓存计算结果,可用 field(init=False) + 在 __post_init__ 中赋值:
@dataclass
class Person:
first: str
last: str
full_name: str = field(init=False)
def __post_init__(self):
if not self.first or not self.last:
raise ValueError("first and last names are required")
self.full_name = f"{self.first} {self.last}"