Generator类型默认不支持.send()类型检查的根本原因是未完整声明三元泛型参数;正确写法是Generator[YieldType, SendType, ReturnType],缺一不可,否则SendType默认为Any,导致类型校验失效。

为什么 Generator 类型默认不支持 .send() 的类型检查?
Python 标准库的 typing.Generator[YieldType, SendType, ReturnType] 确实支持 .send(),但很多人只写 Generator[YieldType](等价于 Generator[YieldType, Any, Any]),这时 .send() 接收参数的类型被推为 Any,IDE 或 mypy 不会校验传入值是否合法,也容易掩盖 bug。
根本原因不是“不支持”,而是类型参数没写全 —— 少了 SendType 和 ReturnType,工具就无法推断 .send() 的输入/输出契约。
正确声明三元泛型:显式写出 SendType 和 ReturnType
只要生成器体内部用了 yield 接收值(即 x = yield y),就必须在类型中明确 SendType;如果生成器可能通过 return expr 终止,且需要捕获返回值,则 ReturnType 也不能是 None。
-
Generator[int, str, bool]表示:每次yield出一个int,接受str类型的.send()输入,最终return一个bool - 若从不
return值(或只return无值),ReturnType可设为None;mypy 默认要求它,不能省略 - 若生成器从不接收
.send()值(即没有x = yield形式),SendType应设为None,此时调用.send(val)会报错(符合预期)
示例:
from typing import Generatordef echo_int() -> Generator[int, str, None]: while True: s = yield 42 # 接收 str,产出 int if s == "stop": return
常见错误:用 Iterator 或 Iterable 替代 Generator
Iterator[T] 只定义了 __next__(),没有 .send() 方法;Iterable[T] 更只保证能 for 循环。两者在类型系统里都不含发送能力,强行调用 .send() 会被 mypy 直接拒绝。
- 不要写
def f() -> Iterator[int]: ...然后在里面用yield x接收值 —— 类型和实现矛盾 - 也不要用
Iterable做返回类型来“绕过”检查,这会让类型提示完全失效 - 即使函数逻辑上是单次生成(如
yield once),只要用了yield表达式(带右赋值),就必须用完整Generator
PyCharm / mypy 实际校验要点
工具对 .send() 的检查非常严格:不仅校验参数类型,还检查是否可能向已终止或未启动生成器发送值(运行时异常,但类型系统不捕获)。重点注意:
- mypy 要求
.send()第一次调用必须传None(除非生成器已用next()或__next__()启动),但类型层面无法强制这个约束 —— 需靠文档或封装函数兜底 - PyCharm 在你写
gen.send("hello")时,会比对SendType;如果类型不符,立刻标红并提示 “Expected type 'str', got 'int' instead” - 若生成器有多个
yield,且接收不同类型(比如yield后有时接int、有时接str),类型系统无法表达这种分支,只能取联合类型如Union[int, str],这时应考虑拆分逻辑或改用类协程
最易被忽略的一点:生成器函数本身返回的是 Generator 实例,但它的类型签名必须写全三个参数,否则所有发送行为在类型层面就“不可见”了 —— 不是语法限制,而是契约缺失。










