学习笔记:搞懂「可选字符串参数」——类型注解 Optional[str] 和默认值 None 分别管什么、合起来表示什么。


一句话总结

def say(state: Optional[str] = None) -> List[str]:
    ...

表示:

  • state 这个参数可以传字符串,也可以不传(或显式传 None
  • 不传时,默认值是 None
  • 类型检查器(如 Pyright、mypy)会允许:say()say(None)say("hello"),但不建议传 say(123)

拆开看:两个部分

这行代码其实包含 两件事,不要混在一起:

state: Optional[str] = None
#      ↑ 类型注解      ↑ 默认值
部分 写法 作用
类型注解 Optional[str] 告诉读代码的人 / 类型检查器:允许的类型
默认参数 = None 调用时不传这个参数,运行时用的值

第一部分:Optional[str] 是什么?

Optional[str] 来自标准库 typing(Python 3.10+ 也可写 str | None):

from typing import Optional

# 下面两种写法等价
Optional[str]
str | None        # Python 3.10+

含义:要么是 str,要么是 None

from typing import Optional

def greet(name: Optional[str]) -> None:
    print(name)

greet("Alice")   # ✅
greet(None)      # ✅
greet(123)       # ⚠️ 类型检查器会警告(运行时不一定报错)

和「只写 str」的区别

def foo(x: str):        # 语义上:期望总是字符串
    pass

def bar(x: Optional[str]):  # 语义上:可以是字符串或 None
    pass

Optional 不会自动帮你处理 None,只是声明「这里可能出现空值」,提醒你要写判断逻辑。


第二部分:= None 是什么?

这是 Python 函数默认参数语法,和类型注解无关,运行时就会生效。

def say(state: Optional[str] = None):
    print(state)

say()           # 没传参 → state 为 None
say("ee")       # 传了 → state 为 "ee"
say(None)       # 显式传 None → state 为 None

如果不写默认值:

def say(state: Optional[str]):  # 没有 = None
    print(state)

say()           # ❌ TypeError: missing 1 required positional argument: 'state'

所以:

  • Optional[str]类型上可以 None
  • = None调用时可以不传参

常见组合 Optional[str] = None = 「可选参数,默认空」


合起来:Optional[str] = None 的完整语义

def say(state: Optional[str] = None) -> List[str]:
    if state:
        return [state]
    return []
调用方式 state 的值 if state
say() None 假,走 return []
say(None) None
say("") "" 假(空字符串在 Python 里是 falsy)
say("ee") "ee" 真,走 return [state]

注意: if state 会把 None"" 都当成「没有有效值」。若空字符串也要处理,应写:

if state is not None:
    ...

和其他写法对比

1. str = "" — 默认空字符串

def foo(x: str = "") -> None:
    pass
  • 类型上不允许 None(除非你用 # type: ignore 硬传)
  • 默认是 "",不是「未提供」

适合:默认就是空文本,例如 locale: str = "zh-CN"

2. Optional[str] 但没有默认值

def foo(x: Optional[str]) -> None:
    pass
  • 可以传 strNone
  • 必须传参,不能 foo() 省略

3. Optional[str] = None — 最常见「可选参数」

def foo(x: Optional[str] = None) -> None:
    pass
  • 可以不传、传 None、传字符串
  • API / 配置类里非常常见

4. 只写 = None 不写 Optional(不推荐)

def foo(x=None):  # 老写法,没有类型信息
    pass

能跑,但 IDE 补全和类型检查帮助较少。新项目建议加上类型注解。


在本项目(xmschain_agent)里的真实例子

src/server/schemas.py 里大量使用了这个模式:

class ChatRequest(BaseModel):
    messages: Optional[List[ChatMessage]] = Field([], ...)
    thread_id: Optional[str] = Field("__default__", ...)
    checkpoint_id: Optional[str] = Field(None, ...)
    enable_clarification: Optional[bool] = Field(None, ...)

含义:

字段 说明
checkpoint_id: Optional[str] = None 可以不传;不传为 None,表示「正常模式」;传了则进入重新生成模式
enable_clarification: Optional[bool] = None 不传时用 State 里的默认值 False
thread_id: Optional[str] = "__default__" 类型可选,但默认是字符串 "__default__",不是 None

Pydantic 模型里:Optional[T] = None 表示该字段不是必填,JSON 里可以省略或为 null


常见使用模式

模式 1:可选配置项

def connect(host: str, port: Optional[int] = None):
    if port is None:
        port = 3306
    ...

模式 2:三态布尔(True / False / 未指定)

enable_clarification: Optional[bool] = None
# None  → 用系统默认
# True  → 强制开启
# False → 强制关闭

模式 3:先判空再使用

def process(name: Optional[str] = None) -> str:
    if name is None:
        return "anonymous"
    return name.upper()

推荐: 判断「有没有传 None」用 is None / is not None;判断「有没有有效内容」才用 if name:


常见误区

误区 1:Optional[str] 会自动把参数变成 None

不会。只有 = None 或调用时传 None 才会是 None。

误区 2:Optional 和「参数可省略」是一回事

  • 可省略 → 需要 默认参数 = None= ""
  • Optional → 只说明类型可以是 None

误区 3:和 return str 混淆

return str        # 返回「类型对象」→ print 出来 <class 'str'>
return "hello"    # 返回字符串值
return str(x)     # 把 x 转成字符串

类型注解里的 str 和返回值里的 str 含义完全不同。


速查表

写法 能否不传参 能否为 None 典型场景
x: str 必填字符串
x: str = "" 默认空串
x: Optional[str] 必传,但可以是 None
x: Optional[str] = None 可选参数(最常见)

小结

state: Optional[str] = None

可以读成:

state 是一个参数;调用时可以不写;不写时等于 None;若写了,可以是字符串或 None

写代码时记得:

  1. is None 判断是否真的没传 / 传了空
  2. if state: 判断是否有「有效非空内容」(空字符串会被当成假)
  3. 项目里 Pydantic、FastAPI、LangGraph 配置类大量用这种写法,读懂它就能看懂 API 请求体定义

记录日期:2026-05-28

更多推荐