尧图精选

CPython C API 中 Py_None 单例对象的设计与使用:从 Py_IsNone 到 Py_RETURN_NONE 的源码级解析

🕒 发布时间:2026/9/5 17:07:48 📁 来源:尧图网络
CPython C API 中 Py_None 单例对象的设计与使用从 Py_IsNone 到 Py_RETURN_NONE 的源码级解析【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython本文围绕 CPython 官方文档 Doc/c-api/none.rst 中定义的None对象 C API 展开Py_None宏、Py_RETURN_NONE返回宏以及为什么 C API 不提供Py_None对应的PyTypeObject和PyNone_Check检查函数。读完本文你将掌握在 C 扩展中正确判断和返回None的惯用写法并理解 3.12 起Py_None成为永生immortal对象后引用计数策略的变化及其在头文件中的具体实现依据。为什么 C API 不暴露 None 的类型对象也没有 PyNone_CheckDoc/c-api/none.rst 开篇就给出了一个重要的 API 设计说明None的PyTypeObject不直接暴露在 Python/C API 中由于None是单例singleton在 C 中直接比较对象指针是否相等即 C 中的恒等性测试就足够了出于同样的原因C API没有提供PyNone_Check函数。这一点可以从源码得到印证。None的类型对象在 CPython 内部命名为_PyNone_Type定义于 Objects/object.c它是一个内部符号并未通过Include下的公开头文件导出给 C 扩展作者而单例本体_Py_NoneStruct在 Objects/object.c 中初始化PyObject _Py_NoneStruct _PyObject_HEAD_INIT(_PyNone_Type);也就是说C 层判断某对象是不是None的标准做法不是查类型而是查指针if (obj Py_None) { /* obj 就是 None */ }这正是 Python 层x is None语义在 C 层的直接对应。Py_None指向 None 单例的宏Doc/c-api/none.rst 对Py_None的定义是The PythonNoneobject, denoting lack of value. This object has no methods and isimmortal.Python 的None对象表示没有值。该对象没有任何方法并且是永生的。同时文档标注了版本变更3.12 起Py_None是 immortal 对象。在头文件 Include/object.h 中其实际展开逻辑为/* _Py_NoneStruct is an object of undefined type which can be used in contexts where NULL (nil) is not suitable (since NULL often means error). */ PyAPI_DATA(PyObject) _Py_NoneStruct; /* Dont use this directly */ #if defined(Py_LIMITED_API) Py_LIMITED_API0 0x030D0000 # define Py_None Py_GetConstantBorrowed(Py_CONSTANT_NONE) #else # define Py_None (_Py_NoneStruct) #endif这里有几个值得注意的实现细节NULL与None的区分。头文件注释明确指出_Py_NoneStruct的存在是为了那些不能用 NULLNULL 通常表示错误的场合。C 扩展返回Py_None表示Python 层的 None而返回NULL表示发生了 C 层错误两者语义完全不同不能混用。稳定 ABIPy_LIMITED_API下的实现差异。当以 3.13 及以上的稳定 ABI 编译时Py_None展开为Py_GetConstantBorrowed(Py_CONSTANT_NONE)——一个借用引用borrowed reference的函数调用不再直接暴露结构体地址Py_CONSTANT_NONE这个常量 ID 在 Objects/object.c 的常量表中对应_Py_NoneStruct。而非受限 API 或 3.12 及以下稳定 ABI 下它直接是取地址_Py_NoneStruct。no methods。None对象在 Python 层确实只有__class__、__doc__、__repr__等类型级行为实例层面不提供任何可调用方法这与文档描述一致。用 Py_IsNone 做恒等性判断除了裸指针比较CPython 还提供了专门的辅助接口。在 Include/object.h 中// Test if an object is the None singleton, the same as x is None in Python. PyAPI_FUNC(int) Py_IsNone(PyObject *x); #define Py_IsNone(x) Py_Is((x), Py_None)其语义与文档中用测试对象恒等性的说法完全一致注释也直接点明它等价于 Python 的x is None。从源码结构看Py_IsNone被导出为一个真实函数见 Objects/object.c以保证在abi3t等不透明对象头的构建下仍然可用。实际编写 C 扩展时Py_IsNone(obj)是比手写obj Py_None更符合 API 惯例的写法。Py_RETURN_NONE 宏immortal 状态下的引用计数差异Doc/c-api/none.rst 还定义了返回宏Py_RETURN_NONE— ReturnPy_Nonefrom a function.从函数中返回Py_None。这个宏看似简单但它的实现精确反映了 3.12 的 immortal 版本变更。在 Include/object.h 中/* Macro for returning Py_None from a function. * Only treat Py_None as immortal in the limited C API 3.12 and newer. */ #if defined(Py_LIMITED_API) Py_LIMITED_API0 0x030c0000 # define Py_RETURN_NONE return Py_NewRef(Py_None) #else # define Py_RETURN_NONE return Py_None #endif两种展开形式的区别在于引用计数老路径Py_LIMITED_API 3.12Py_None还是普通对象函数返回的是新引用必须用Py_NewRef(Py_None)递增一次引用计数否则调用方持有引用后对象会少计一次。新路径默认或稳定 ABI 3.12Py_None是 immortal 对象Py_INCREF对永生对象是无操作直接return Py_None即可。Immortal 对象在 CPython 中的实现策略文档中immortal一词值得展开。CPython 对永生对象有一套完整的引用计数策略说明位于 Include/refcount.h其核心思想是在 64 位系统上引用计数低 32 位达到2**31及以上的对象被视为 immortal_Py_IMMORTAL_MINIMUM_REFCNT初始值设为3 30_Py_IMMORTAL_INITIAL_REFCNT这样设计保证了向后兼容用旧版Py_INCREF/Py_DECREF的 C 扩展如针对 3.11 及更早编译的 abi3 模块继续增减引用计数时即使计数偏移约 10 亿次也不会跌破永生阈值执行仍然正确在 32 位系统上阈值降低为2**30引用计数递增使用饱和算术saturated arithmetic保证永生对象的计数不会溢出。正因为Py_None作为静态分配的永生单例永远不会被释放3.12 起的Py_RETURN_NONE才能省去Py_NewRef这一步——文档中的versionchanged:: 3.12标注与头文件中的条件编译分支是相互印证的。实践要点小结综合 Doc/c-api/none.rst 的规范与上述源码证据C 扩展中与None打交道的正确姿势可以归纳为获取直接使用Py_None宏不要手写_Py_NoneStruct头文件注释明确标注了 Dont use this directly判断优先用Py_IsNone(obj)其语义等价于 Python 的x is None不需要也不应该寻找PyNone_Check返回使用Py_RETURN_NONE宏让头文件按当前 API 版本自动选择是否需要Py_NewRef不要暴露类型_PyNone_Type是内部实现C API 层面判断None恒等即可这是官方文档明确的设计决策而非遗漏。以上写法以当前 CPython 仓库的头文件与实现为准如果你的扩展面向 3.11 及更早版本Py_LIMITED_API 0x030c0000Py_None仍按普通引用计数对象处理Py_RETURN_NONE会自动补上Py_NewRef无需手写。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →