功能说明
Signup.__post_init__ 在对象构造完成后立即校验字段:邮箱去空格并转小写,年龄必须是真正的整数且在 13 到 120 之间,用户名长度 3 到 20 且只允许字母、数字与下划线。所有错误先收集到字典里,最后一次性抛出 ValidationError,因此调用方可以一次看到全部问题,而不是修一个再报一个。
关键代码
if isinstance(self.age, bool) or not isinstance(self.age, int):
errors["age"] = "must be an integer"
elif not 13 <= self.age <= 120:
errors["age"] = "must be between 13 and 120"
...
if errors:
raise ValidationError(errors)
在 Python 中 bool 是 int 的子类,所以必须先把 True 排除,否则它会被当成年龄 1。
使用注意
__post_init__在__init__之后运行,对普通数据类有效;使用frozen=True时需要改用object.__setattr__才能归一化字段。- 校验只发生在构造时,之后直接修改属性不会被再次检查。
- 错误字典的插入顺序就是字段检查顺序,输出稳定。
- 归一化后的值写回
self,调用方拿到的对象已经是清理过的数据。 - 复杂项目可以换成 Pydantic 或 attrs 的校验器,但这里没有额外依赖。
验证方式
示例应打印一个邮箱已转小写、用户名已去空格的 Signup 对象;第二组参数会触发三条错误,异常消息以分号连接,error.errors 中可以按字段读取。可以分别传入 True、12 和带空格的用户名验证边界。