在 FastAPI 与 Pydantic 的学习过程中,Union、Optional、Field 是三个高频出现但极易混淆的知识点。很多学习者会有这样的疑问:“Union 不也能表示没有值吗?”“为什么有了 Union 还要用 Optional?”“Field 和前两者到底有什么关系?”

本文基于之前的探讨,做一次全面总结,用“本质+场景+示例”的方式,帮你彻底分清三者的作用、区别,以及在实际开发中如何规范使用,避免踩坑,真正做到学以致用。

目录

一、核心前提:三者本质完全不同,各司其职

二、逐个拆解:每一个的核心作用与用法

1. Union:多类型选择器(可包含空值,但不止于空值)

核心特点

实用代码示例

代码解释

2. Optional:可空字段的“语法糖”(专门用于空值)

核心特点

实用代码示例

代码解释

3. Field:字段校验与描述工具(和类型无关)

核心特点

实用代码示例

代码解释

三、关键区别:一张表彻底分清

四、高频混淆点解答(必看)

1. 疑问:Union 也能表示可空,为什么还要用 Optional?

2. 疑问:Optional 和 Field 都能控制“可空”,区别是什么?

3. 疑问:三者可以一起使用吗?

五、实战总结与使用建议


一、核心前提:三者本质完全不同,各司其职

首先要明确一个核心认知:Union 和 Optional 属于 Python 类型注解,用于定义字段的“类型规则”;而 Field 属于 Pydantic 工具,用于定义字段的“校验规则和描述信息”,三者没有替代关系,反而经常搭配使用。

先给大家一句终极记忆口诀,记牢核心区别:

Union 管“类型选择”,Optional 管“空值允许”,Field 管“校验规则”

二、逐个拆解:每一个的核心作用与用法

1. Union:多类型选择器(可包含空值,但不止于空值)

Union 来自 Python 内置的 typing 模块,核心作用是 允许一个字段取多种类型中的任意一种,它的范围很广,“可空”只是它的一个特例(当类型中包含 None 时)。

核心特点
  • 用于限定“字段可以是什么类型”,支持多种类型(2种及以上);

  • 可以包含 None,此时等价于“可空”,但不是专门用于“可空”;

  • 不涉及任何校验规则,也不控制字段是否必填,只关注“类型”。

实用代码示例
from typing import Union
from fastapi import FastAPI

app = FastAPI()

# 示例1:多种类型(int / str),不可空
@app.get("/data/{value}")
def get_data(value: Union[int, str]):
    return {"传入值": value, "类型": type(value).__name__}

# 示例2:包含None(可空),等价于 Optional[str]
@app.get("/info/")
def get_info(name: Union[str, None] = None):
    return {"name": name}
代码解释

示例1中,value 可以是 int(如 123)或 str(如 "abc"),但不能是 float(如 3.14),也不能不传;示例2中,name 可以是 str,也可以是 None(不传),这是 Union 实现“可空”的场景,但它的核心能力还是“多类型选择”。

2. Optional:可空字段的“语法糖”(专门用于空值)

Optional 同样来自 typing 模块,它的本质是 Union[类型, None] 的简写,专门用于表达“字段可以传值,也可以不传(值为 None)”,语义更明确、写法更简洁。

核心特点
  • 专门用于“可空”场景,只能是“单一类型 + None”,不能用于多类型选择;

  • 等价于 Union[类型, None],但比 Union 更直观,一眼就能看出“这个字段可空”;

  • 不涉及校验,只控制“字段是否可以为 None”,和“是否必填”相关(通常配合默认值 None 实现可选参数)。

实用代码示例
from typing import Optional
from fastapi import FastAPI

app = FastAPI()

# 示例1:最常用场景,可空可选参数
@app.get("/items/")
def read_items(q: Optional[str] = None):
    return {"q": q}

# 示例2:错误用法(Optional 不能用于多类型)
# def read_user(id: Optional[int, str]):  # ❌ 报错
#     pass
代码解释

示例1中,q 是 Optional[str],等价于 Union[str, None],可以不传(值为 None),也可以传字符串;示例2是错误用法,因为 Optional 只能接收一个类型参数,不能实现多类型选择,此时必须用 Union。

3. Field:字段校验与描述工具(和类型无关)

Field 来自 Pydantic 模块,它 不是类型注解,核心作用是给字段添加“校验规则、描述信息、默认值”,不改变字段的类型,只对字段的值进行约束。

核心特点
  • 不改变字段类型,必须配合类型注解(Union、Optional 或单一类型)使用;

  • 支持丰富的校验规则(如数值范围、字符串长度、正则匹配等);

  • 可添加字段描述,用于自动生成 API 文档(FastAPI 会识别 Field 中的 description);

  • 控制字段的“必填性”(用 ... 表示必填,None 表示默认空值)。

实用代码示例
from pydantic import BaseModel, Field
from typing import Optional, Union

# 结合 Pydantic 模型使用(最常用场景)
class Product(BaseModel):
    # 类型:str,可空,校验:长度2-20,必填
    name: Optional[str] = Field(..., min_length=2, max_length=20, description="商品名称")
    # 类型:int/str,校验:大于0,默认值None
    price: Union[int, float] = Field(None, gt=0, description="商品价格,必须大于0")
代码解释

name 是 Optional[str](可空),通过 Field 限定了长度范围(2-20),且用 ... 表示必填(即使可空,也必须传入 None 或符合规则的字符串);price 是 Union[int, float](多类型),通过 Field 限定价格必须大于0,默认值为 None(可选参数)。这里 Field 只做校验和描述,不改变字段的类型。

三、关键区别:一张表彻底分清

名称

来源

核心作用

是否改变类型

是否涉及校验

典型场景

Union

Python typing

允许字段取多种类型(可选包含 None)

✅ 改变(多类型可选)

❌ 不涉及

字段可以是 int/str 二选一、int/str/None 三选一

Optional

Python typing

专门表示字段可空(类型 + None)

✅ 改变(增加 None 类型)

❌ 不涉及

接口可选参数、模型可空字段

Field

Pydantic

字段校验、描述、默认值设置

❌ 不改变

✅ 核心功能

限定数值范围、字符串长度、生成API文档描述

四、高频混淆点解答(必看)

1. 疑问:Union 也能表示可空,为什么还要用 Optional?

答:Union 是“多类型选择器”,可空只是它的一个特例;而 Optional 是“可空场景的专用语法糖”。用 Optional 更直观、更规范,别人一看就知道你的意图是“这个字段可空”,而不是“这个字段有多种类型”。

比如:Optional[str] 比 Union[str, None] 更简洁,语义更清晰,这也是 Python 官方推荐的写法。

2. 疑问:Optional 和 Field 都能控制“可空”,区别是什么?

答:Optional 是“类型层面”的可空(允许字段值为 None),不做任何校验;Field 是“校验层面”的可空(通过默认值 None 实现),同时可以添加额外校验规则。大白话就是Optional 只 “允许为空”,不管其他;Field 既 “允许为空”,还能给 “非空的情况” 定规矩。

比如:Optional[str] 只表示“可以是 str 或 None”,但不能限制字符串长度;而 Field(None, min_length=2) 不仅允许 None,还能限制非 None 时的字符串长度。

3. 疑问:三者可以一起使用吗?

答:当然可以,而且是实际开发中的高频用法!通常的搭配是:Union/Optional 定义类型,Field 定义校验和描述。

from typing import Union, Optional
from pydantic import BaseModel, Field

class User(BaseModel):
    # 类型:int/str,可空,校验:长度≥1,必填
    user_id: Optional[Union[int, str]] = Field(..., min_length=1, description="用户ID")

五、实战总结与使用建议

结合 FastAPI 和 Pydantic 的实际开发场景,给大家3条实用建议,避免踩坑:

  1. 当你只想表达“字段可空、可选”时,用 Optional + 类型 + 默认值 None,规范又直观(如:q: Optional[str] = None);

  2. 当你需要字段支持多种类型(无论是否可空)时,用 Union;若同时需要可空,在 Union 中加入 None(如:data: Union[int, str, None]);

  3. 当你需要给字段加校验规则、描述信息时,用 Field,并且必须配合类型注解(Union/Optional/单一类型)使用,不要单独使用 Field。

最后再强调一句:Union 和 Optional 管“类型”,Field 管“校验”,三者分工明确、相辅相成。掌握它们的用法,能让你的 FastAPI 接口更规范、更健壮,也能让你的 Pydantic 模型更清晰、更易维护。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐