汉字魔法师
118.67M · 2026-02-04
在 Python 的类型标注(type hints)体系中,Optional 是一个非常常用、也非常容易被误解的工具。很多人以为 Optional[T] 的意思是“这个参数可选、可以不传”,但实际上它表达的是:值可能是 T,也可能是 None。
这篇文章会把 Optional 的语义、典型用法、与默认参数/可选参数的关系,以及静态类型检查中的注意事项讲清楚。
Optional[T] 定义在 typing 模块中:
from typing import Optional
它的含义是:
也就是说:
Optional[int] 表示 int 或 NoneOptional[str] 表示 str 或 NoneT | Nonedef parse_age(s: str) -> int | None:
...
Optional[T] / Union[T, None]from typing import Optional, Union
def parse_age(s: str) -> Optional[int]:
...
def parse_age2(s: str) -> Union[int, None]:
...
在语义上这三种写法完全等价。团队如果有兼容性要求(例如要支持 3.9),通常用 Optional[T] 更稳妥。
这是最常见误解。
from typing import Optional
def f(x: Optional[int]): # 没默认值
...
这里 x 必须传,只是传入的值允许是 int 或 None:
f(1) # OK
f(None) # OK
f() # TypeError:缺少参数
def f(x: Optional[int] = None):
...
这时才是“可不传”,并且值也允许为 None。
例如查找失败返回 None:
def find_user_name(user_id: int) -> Optional[str]:
if user_id == 1:
return "Alice"
return None
调用方就需要处理 None 分支:
name = find_user_name(2)
if name is None:
print("not found")
else:
print(name.upper())
例如可选过滤条件:
from typing import Optional
def query_users(country: Optional[str] = None) -> list[str]:
if country is None:
return ["Alice", "Bob"]
return ["Alice"]
from dataclasses import dataclass
from typing import Optional
@dataclass
class User:
id: int
email: Optional[str] # 有些用户可能没有邮箱
Optional[str] 意味着变量可能是 None,因此你不能直接当 str 用,否则类型检查器(mypy/pyright)会报错。
from typing import Optional
def shout(name: Optional[str]) -> str:
return name.upper() # 类型检查会报:name 可能是 None
def shout(name: Optional[str]) -> str:
if name is None:
return "UNKNOWN"
return name.upper()
这种 if name is None 会触发类型收窄(narrowing):
if 分支里:name 是 Noneelse 分支里:name 是 strdef shout(name: Optional[str]) -> str:
if name is None:
raise ValueError("name required")
return name.upper()
很多人写:
if name:
...
这会把以下值都当成“空”:
None""(空字符串)"0" 是 truthy,但 0 是 falsy0、0.0、False、空容器等如果你的意图是只判断 None,要写:
if name is None:
...
这是 Optional 场景里非常推荐的写法。
Optional[list[int]] vs list[Optional[int]]这两个差别极大:
Optional[list[int]]列表本身可能不存在:
xs: Optional[list[int]] = None # OK
xs = [1, 2, 3] # OK
list[Optional[int]]列表一定存在,但元素可能是 None:
xs: list[Optional[int]] = [1, None, 3]
实际项目里两者经常写反,建议写之前先问自己一句:
Python 里可变默认值是大坑:
def add_item(x, items=[]): # 不推荐
items.append(x)
return items
正确做法常用 Optional[list[T]] = None 作为哨兵(sentinel):
from typing import Optional
def add_item(x: int, items: Optional[list[int]] = None) -> list[int]:
if items is None:
items = []
items.append(x)
return items
这里 None 的意义是“没传就新建”。
有些场景 None 既可能表示“缺失”,也可能是“合法值”(例如某字段允许显式为 None)。这时可以用自定义 sentinel 区分:
_MISSING = object()
def set_value(x=_MISSING):
if x is _MISSING:
print("not provided")
else:
print(f"provided: {x!r}")
类型标注上更严格的写法会更复杂(涉及 object、Literal、overload 等),但思想很重要:当 None 的语义不够用时,考虑 sentinel。
Optional[T] 表示 T 或 None,不是“参数可不传”x: Optional[T] = NoneOptional,调用前/使用前要做 is None 判断if x:,用 if x is None:Optional[list[T]](容器可无)和 list[Optional[T]](元素可无)Optional[...] = None 常用于避免可变默认值问题