PEP 737:用于格式化类型全限定名的 C API

PEP 737:用于格式化类型全限定名的 C API

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

内容提要

该PEP为C API新增格式化类型全限定名的功能:新增PyType_GetFullyQualifiedName()和PyType_GetModuleName(),并为PyUnicode_FromFormat()增加%T、%#T、%N、%#N格式,避免使用易致崩溃的借用引用,且兼容受限C API。建议新C代码在错误信息和__repr__中使用全限定名、不再截断类型名,以消除C与Python实现输出不一致的问题。Python层改动(如__fully_qualified_name__)被指导委员会否决。

🔎

延伸解读

C 与 Python 类型名输出不一致的根源

文章指出,C 代码常用 Py_TYPE(obj)->tp_name 获取类型名,而该成员的值取决于类型是用 C 还是 Python 实现。例如 datetime.date 在 C 实现下报错显示 datetime.date,在 Python 实现下只显示 date。这违背了 PEP 399 要求纯 Python 与 C 加速模块行为一致的原则。新 C API 旨在让 C 代码也能输出与 Python 层 f"{type.__module__}.{type.__qualname__}" 一致的全限定名,从而消除这种实

新格式符如何避免借用引用崩溃

文章用具体示例说明风险:若用 %T 直接传入 Py_TYPE(obj) 这一借用引用,在格式化 %R 时可能触发 __repr__ 并释放该类型的最后一个引用,导致后续 %T 访问悬空指针而崩溃。新增的 %T 格式改为接收对象本身,内部再取类型,从而避免调用方持有借用引用。同时 %N 用于直接格式化类型对象。这些格式兼容受限 C API,比手动调用 PyType_GetQualName() 并管理 Py_DECREF() 更安全便捷。

截断类型名的历史包袱与迁移建议

文章回顾,PyErr_Format() 早期使用固定 500 字节缓冲区,开发者因此习惯用 %.100s 截断类型名;2001 年改为动态分配后,这一做法仍被保留,但原因已被遗忘。Python 层并不截断类型名,C 层截断同样违背 PEP 399。PEP 建议新 C 代码不再截断类型名,改用 %s 或 %T,并在错误信息和 __repr__ 中使用全限定名,以区分不同模块中的同名类型。现有代码无需改动,保持向后兼容。

Python 层提案被否决的边界

该 PEP 的 Python 层改动均被指导委员会否决,包括新增 type.__fully_qualified_name__ 属性、type.__format__() 的 N/#N 格式,以及修改 type.__str__() 返回全限定名。委员会认可 C API 改动的价值,但质疑 Python 层改动的必要性,尤其对 __fully_qualified_name__ 存疑。因此读者应注意,本 PEP 最终落地范围仅限于 C API 与 PyUnicode_FromFormat() 的新格式,Python 层行为

❓

Q&A

PEP 737 新增了哪些 C API 来格式化类型全限定名?

新增了 PyType_GetFullyQualifiedName() 和 PyType_GetModuleName() 两个函数,并为 PyUnicode_FromFormat() 增加了 %T、%#T、%N、%#N 格式。

为什么在 C 代码中推荐使用类型全限定名而不是短类型名?

因为短类型名可能在不同模块中重复,导致歧义;全限定名能明确标识类型,且避免 C 与 Python 实现输出不一致的问题。

使用 Py_TYPE(obj)->tp_name 格式化类型名有什么问题?

tp_name 的输出取决于类型是用 C 还是 Python 实现的:C 类型给出全限定名,Python 类型给出短名,这违反了 PEP 399 的兼容性原则。此外,tp_name 不能用于受限 C API。

PEP 737 中 %T 和 %N 格式有什么区别?

%T 用于格式化对象的类型全限定名(等价于 PyType_GetFullyQualifiedName(Py_TYPE(obj))),而 %N 用于格式化类型本身的全限定名(等价于 PyType_GetFullyQualifiedName(type))。

为什么在 C 代码中应避免截断类型名?

截断类型名(如使用 %.100s)是历史遗留做法,会导致 C 与 Python 输出不一致,且可能丢失重要信息;新代码应使用 %s 或 %T 格式,不截断类型名。

PEP 737 是否引入了 Python 层面的改动?

没有。原本提议的 Python 层改动(如添加 type.__fully_qualified_name__ 属性)被指导委员会否决,因此 PEP 只包含 C API 的增强。

🏷️

标签

➡️

继续阅读