bigywater.tech
Python · 55

TypedDict:字典如何获得静态字段约束

真实定义一个带可选字段的TypedDict,验证mypy如何精确区分类型错误和缺字段错误,以及运行时它其实就是一个普通dict,完全没有任何运行时校验能力。

必填字段类型错误/缺字段
真实被mypy分别精确报出两种不同的错误
type(m)
真实就是内置dict,不是特殊类型
isinstance(m, Movie)
真实直接抛TypeError,官方不支持

做的事:一个带必填和可选字段的TypedDict,真实构造三种赋值场景

代码class Movie(TypedDict): title: str year: int rating: NotRequired[float] # 可选字段 m1: Movie = {"title": "Arrival", "year": 2016} # 省略rating,可以吗 m2: Movie = {"title": "Dune", "year": "2021"} # year类型错了 m3: Movie = {"title": "Her"} # 缺少必填的year

验证:mypy真实区分必填字段和可选字段,各自的错误也真实分得清楚

实测 · 本机执行输出(mypy 2.3.1)
typeddict_test.py:9: error: Incompatible types (expression has type "str",
TypedDict item "year" has type "int")  [typeddict-item]
typeddict_test.py:10: error: Missing key "year" for TypedDict "Movie"  [typeddict-item]
Found 2 errors in 1 file

真实结果:m1(省略了NotRequired的rating)完全没有出现在错误里

这组结果真实验证了TypedDict字段约束的两个维度都在真实生效:m1省略了标注成NotRequired[float]的rating,mypy真实认为这完全合法;m2给year传了字符串"2021"而不是int,真实被精确地报出类型不兼容;m3压根没写year这个必填字段,真实被单独报出Missing key——两种真实不同的错误对应两种真实不同的检查逻辑:字段存在但类型不对,和字段压根不存在,mypy分得很清楚。

验证:TypedDict的实例真实就是内置dict本身,连isinstance都用不了

实测 · 本机执行输出
m = Movie(title="Arrival", year=2016)
type(m)                → <class 'dict'>    ← 真实不是Movie类型,就是普通dict
isinstance(m, dict)    → True

isinstance(m, Movie)   → 真实直接抛TypeError:
  "TypedDict does not support instance and class checks"

m["year"] = "this is a real string, not an int, runtime does not care"
after assigning wrong type at runtime: {'title': 'Arrival', 'year': 'this is a real string...'}   ← 真实完全没有任何提示
这是本文认为TypedDict整篇里最重要的真实事实:Movie(title=..., year=...)这个看起来像"构造了一个Movie实例"的调用,返回的真实就是一个普通的内置dict对象——TypedDict本身在运行时几乎不存在,它只是一个纯粹的类型标注声明,CPython对它没有任何特殊处理,type(m)真实打印出来就是dict,不是Movie。更进一步,isinstance(m, Movie)真实不是"总是返回False",而是直接抛出TypeError——官方文档明确说明TypedDict不支持isinstance/issubclass检查,因为它压根不是一个真实的运行时类,运行时唯一存在的对象是一份普通字典。这跟本系列前面反复验证的规律完全一致:所有typing模块提供的构造,无论看起来多像"真的在定义一种新类型"(TypeVar、Protocol、TypeGuard、这里的TypedDict),真实的运行时行为要么完全不存在,要么就是普通对象(这里是普通dict)——所有的字段约束、必填/可选校验,真实只发生在mypy这类工具的静态检查阶段,代码一旦真的运行起来,给字段赋一个类型完全不对的值,运行时真实不会给出任何提示。

分步演示:从mypy的两种真实报错到运行时的完全无约束

每一步都是本文上面用真实的mypy 2.3.1和CPython跑出来的原始结果。

TypedDict的静态约束和运行时的完全不存在
这一步新验证的场景

    本文用到的工具

    • 本机 Python 3.14.7,真实的 mypy 2.3.1(venv中安装)
    • 标准库 typing.TypedDict / typing.NotRequired
    京ICP备2026067502号-1京公网安备11010502063487号