
本文介绍如何在 Pydantic v2 中,通过预验证(@field_validator(mode='before'))将输入数据中的 float('nan') 统一转为 None,确保 model_dump() 输出符合预期(如 [250.5, 251.0, None]),避免原始 NaN 值污染序列化结果。
本文介绍如何在 pydantic v2 中,通过预验证(`@field_validator(mode='before')`)将输入数据中的 `float('nan')` 统一转为 `none`,确保 `model_dump()` 输出符合预期(如 `[250.5, 251.0, none]`),避免原始 nan 值污染序列化结果。
在使用 Pydantic 构建数据模型时,float('nan') 是一个特殊浮点值,它既不等于任何数(包括自身),也无法被 JSON 或大多数序列化格式原生支持。当 model_dump() 遇到 NaN,默认会保留为 nan(在 Python 字典中表现为 float('nan')),而该值在 JSON 序列化(如 json.dumps())中会引发 TypeError: Object of type float is not JSON serializable —— 这正是开发者常需规避的问题。
Pydantic 本身不提供全局开关(如 model_dump(nan_as_none=True))来自动替换 NaN,但可通过字段级预验证优雅解决。核心思路是:在数据进入模型实例化前(即 mode='before' 阶段),对含 NaN 的字段(如 list[float | None])进行清洗,将每个 math.isnan(x) 为 True 的元素替换为 None。
以下是一个完整、可直接运行的示例:
import math
from pydantic import BaseModel, field_validator
class FooClass(BaseModel):
foo: list[float | None] # 类型声明允许 None,为后续转换提供类型安全
@field_validator('foo', mode='before')
@classmethod
def convert_nan_to_none(cls, values: list) -> list:
if not isinstance(values, list):
return values
return [None if isinstance(v, float) and math.isnan(v) else v for v in values]
# 测试数据包含 NaN
invalid_data = {"foo": [250.5, 251.0, float("nan")]}
data = FooClass(**invalid_data)
dict_data = data.model_dump()
# ✅ 断言通过
assert dict_data["foo"] == [250.5, 251.0, None]
print(dict_data) # {'foo': [250.5, 251.0, None]}
✅ 关键要点说明:
-
mode='before'确保验证发生在类型转换之前,此时原始float('nan')仍保持为float类型,可被math.isnan()正确识别; - 类型注解
list[float | None]不仅语义清晰,还让 Pydantic 在后续校验/序列化中接受None元素; - 使用列表推导式替代显式循环,代码更简洁且性能更优;
- 若字段可能为嵌套结构(如
list[dict[str, float | None]]),需递归处理或改用自定义@model_validator(mode='before')实现深度清洗。
⚠️ 注意事项:
- 此方案作用于输入解析阶段,而非
model_dump()调用时。因此model_dump()输出已天然不含 NaN,无需额外后处理; -
math.isnan()仅对float类型有效,务必先检查isinstance(v, float),避免对None、字符串等调用时报错; - 若需统一处理所有数值字段(如
int,float,Decimal),建议封装为通用工具函数并在多个@field_validator中复用。
综上,通过 @field_validator(mode='before') 对 NaN 进行前置标准化,是 Pydantic v2 中兼顾类型安全、序列化兼容性与代码可维护性的推荐实践。










