PEP 846:类型别名的文档字符串

PEP 846:类型别名的文档字符串

💡 原文英文,约1900词,阅读约需7分钟。
📝

内容提要

该PEP提议将type语句后的字符串字面量保存为类型别名的__doc__属性,并通过ast、pydoc和help()展示。解析器将其存入ast.TypeAlias新增的doc字段,ast.get_docstring()可读取。规则适用于各类作用域,仅限字符串常量,-OO优化会剥离。无需类型检查器调整,但需更新相关工具。

🔎

延伸解读

与现有工具生态的衔接

该提案并非凭空创造新约定,而是将Pyright、Sphinx和Pylint等工具已支持的“类型别名后置文档字符串”写法正式纳入语言运行时。这意味着现有代码中符合该位置的字符串,在Python 3.16中会自动成为别名的__doc__,无需作者改写。但需注意,只有type语句创建的别名获得此能力,用typing.TypeAlias注解的普通赋值不会。

对AST使用者的影响

文档字符串不再作为独立的ast.Expr节点存在,而是存入ast.TypeAlias新增的doc字段,且节点结束位置会覆盖该字符串。依赖“别名后一条语句”来查找文档的工具,或依赖别名节点end_lineno的工具,在解析Python 3.16代码时需要调整。用三个位置参数构造ast.TypeAlias仍可工作,但读取文档应改用doc字段或ast.get_docstring()。

运行时行为与优化剥离

别名的__doc__可在创建后重新赋值,删除则重置为None。访问该属性不会触发别名值的求值。与函数和类文档字符串一致,-OO或optimize=2会剥离别名文档字符串,使__doc__变为None,且AST预处理会清空doc字段;优化级别0和1则保留。对__doc__的运行时赋值不受-OO影响。

设计取舍与边界

提案选择在解析器层面识别文档字符串,而非保留独立Expr节点,避免了AST中同一文档出现两份、转换后不一致的问题。仅限字符串常量:相邻字符串拼接或带括号的字符串字面量合格,但字节串、f-string、t-string及“first”+“second”等表达式不合格。只有紧随type语句的第一个字符串语句成为__doc__,后续字符串按PEP 257仍为普通表达式语句。

❓

Q&A

PEP 846 提议为类型别名添加什么新特性?

该 PEP 提议将 type 语句后紧跟的字符串字面量保存为类型别名对象的 __doc__ 属性,并通过 ast、pydoc 和 help() 展示。

类型别名的文档字符串在 AST 中是如何存储的?

解析器将文档字符串存储在 ast.TypeAlias 新增的可选 doc 字段中,而不是创建单独的 ast.Expr 节点。ast.get_docstring() 可以从该字段读取别名文档字符串。

哪些字符串形式可以作为类型别名的文档字符串?

必须是表达式语句,其值为字符串常量。相邻字符串字面量拼接或带括号的字符串字面量符合条件;字节字面量、f-string、t-string 以及像 "first" + "second" 这样的表达式不符合条件。

在 -OO 优化下,类型别名的文档字符串会被如何处理?

优化级别 2(-OO 或 compile(..., optimize=2))会像剥离函数和类文档字符串一样剥离别名文档字符串,结果别名的 __doc__ 为 None。在 AST 中,预处理会清除 ast.TypeAlias 的 doc 字段。优化级别 0 和 1 则保留文档字符串。

help() 函数现在如何显示类型别名的文档?

pydoc(包括 help())将识别类型别名并显示其自身的文档字符串,同时将别名与模块文档中的其他数据成员区分开。例如,help(Timeout) 会显示别名声明及其文档字符串。

这个提案对类型检查器有影响吗?

不需要对类型检查器行为做任何更改。别名的文档字符串不影响其对类型检查器的含义,也不影响其值在运行时的求值方式。

如何通过 TypeAliasType 构造函数为别名设置文档字符串?

TypeAliasType 构造函数新增了一个仅限关键字的 doc 参数,默认值为 None,用于初始化 __doc__,且不进行空白处理。例如:Timeout = TypeAliasType("Timeout", float | None, doc="Maximum wait in seconds.")。

类型别名的文档字符串可以重新赋值或删除吗?

可以。别名创建后,其 __doc__ 属性可以重新赋值;删除它会将其重置为 None。对 __doc__ 的赋值是普通的运行时赋值,不会被 -OO 剥离。

🏷️

标签

➡️

继续阅读