首页
/ CPython 模块对象 C API 详解:PyModule_Type、PyModuleDef、Slot 定义与模块状态管理

CPython 模块对象 C API 详解:PyModule_Type、PyModuleDef、Slot 定义与模块状态管理

2026-09-04 20:15:47作者:尤峻淳Whitney

本文基于 CPython 官方文档 Module Objects 展开,系统讲解 CPython C API 中“模块对象”这一核心主题:PyModule_Type 类型对象、PyModule_* 查询与创建函数、3.15 起推荐的 PySlot 数组(slots)模块定义方式、传统的 PyModuleDef 结构体方式、模块状态(module state)的生命周期管理、模块 token 校验,以及模块初始化用的辅助函数(PyModule_AddObjectPyModule_AddFunctions 等)。读完本文,你可以在 C 扩展中正确创建模块、声明模块级函数与状态、支持子解释器(sub-interpreters)与 free-threaded 构建,并理解这些 API 在 Objects/moduleobject.cInclude/moduleobject.h 中的实际实现。

1. 模块对象与 PyModule_Type

Python 中每个可导入的单元都是一个模块对象。CPython 为其定义了专门的类型:

  • PyTypeObject PyModule_Type:代表 Python 模块类型的 PyTypeObject 实例,对 Python 程序而言暴露为 types.ModuleType

两个最常用的类型检查宏,其定义见 Include/moduleobject.h

函数/宏 语义
int PyModule_Check(PyObject *p) p 是模块对象或模块对象的子类型则返回真,恒不失败
int PyModule_CheckExact(PyObject *p) p 是“精确”的模块对象(PyModule_Type 本身,而非子类型)则返回真,恒不失败

从源码看,两者都只是对类型指针的宏封装:PyModule_Check(op) 展开为 PyObject_TypeCheck((op), &PyModule_Type)(允许子类型),而 PyModule_CheckExact(op) 展开为 Py_IS_TYPE((op), &PyModule_Type)(要求精确类型)。注意 PyModule_CheckExact 的文档表述为“是模块对象、但不是 PyModule_Type 的子类型”,这一点容易误读,写代码时应以“精确类型检查”为准。

2. 模块的创建与查询 API

2.1 创建模块:PyModule_NewObject / PyModule_New

函数 说明
PyObject *PyModule_NewObject(PyObject *name) 创建一个新模块对象并设置其 __name__name;同时填充 __doc____package____loader__(除 __name__ 外均设为 None)。调用方负责自行设置 __file__。出错时返回 NULL 并设置异常。3.3 新增;3.4 起 __package____loader__ 才被设为 None
PyObject *PyModule_New(const char *name) PyModule_NewObject 相同,但 name 是 UTF-8 编码的 C 字符串而非 Unicode 对象

实现位于 Objects/moduleobject.cPyModule_New 先用 PyUnicode_FromString 把 UTF-8 名称转为 Unicode 对象,再转调 PyModule_NewObjectPyModule_NewObject 内部经由 new_module_notrack() 分配对象,md_dict = PyDict_New() 创建命名空间字典,再由 module_init_dict() 一次性写入 __name____doc____package____loader____spec__ 等内置属性(docNULL 时写 Py_None)。这与文档描述一致:__file__ 不由创建函数设置,而是留给调用方(或导入机制)填充。

2.2 查询模块属性

函数 返回值与错误
PyObject *PyModule_GetDict(PyObject *module) 返回实现模块命名空间的字典对象,与模块的 __dict__ 属性相同。若参数不是模块对象则抛出 SystemError 并返回 NULL。返回的是借用的引用,模块销毁前有效
PyObject *PyModule_GetNameObject(PyObject *module)(3.3+) 返回模块的 __name__;若未定义或不是字符串,抛 SystemError 并返回 NULL
const char *PyModule_GetName(PyObject *module) 同上,但返回 'utf-8' 编码的 C 字符串。缓冲区仅在模块被重命名或销毁前有效;注意 Python 代码可以通过设置 __name__ 属性重命名模块
PyModuleDef *PyModule_GetDef(PyObject *module) 返回模块所由创建的 PyModuleDef 结构体指针;若不是从定义创建的则返回 NULL。出错时也返回 NULL,需配合 PyErr_Occurred() 区分“无定义”与“出错”
PyObject *PyModule_GetFilenameObject(PyObject *module)(3.2+) 通过模块的 __file__ 属性返回模块加载来源文件的名称(Unicode 对象);未定义或不是字符串时抛 SystemError
const char *PyModule_GetFilename(PyObject *module) 返回 UTF-8 编码的文件名,仅在 __file__ 被重新赋值或模块销毁前有效。3.2 起弃用:不可编码的文件名会引发 UnicodeEncodeError,应改用 PyModule_GetFilenameObject

文档明确建议:扩展应优先使用其他 PyModule_*PyObject_* 函数,而不是直接操纵模块的 __dict__(即避免依赖 PyModule_GetDict 拿到的裸字典)。

3. 模块定义:从 PyModuleDef 到 Slot 数组

CPython 中“如何创建一个模块”的描述方式有两种:slots 数组(3.15 起的推荐方式)与 PyModuleDef 结构体(传统方式,仍受支持、暂无移除计划)。

3.15+ 推荐: PySlot 数组(Py_mod_name / Py_mod_abi / Py_mod_exec / ...)
                │
                ▼
        PyModule_FromSlotsAndSpec() + PyModule_Exec()

传统方式:   static PyModuleDef mymoduledef = { ... }
                │
                ▼
        PyModule_Create() / PyModule_FromDefAndSpec()
        (m_slots 允许重复的 Py_mod_exec,按出现顺序执行)

3.1 Slot 数组总述

用 C API 创建的模块,通常用一个 PySlot 结构体数组来定义,该数组是“如何创建模块”的描述(slots 机制的通用说明见 Slots)。slots 数组一般用于定义扩展模块的“主”模块对象(参见 extension-modules),也可用于动态创建扩展模块(见第 6 节)。

规则:除非另有说明,同一个 slot ID 在数组中不允许重复。 这与传统 PyModuleDef.m_slots 形成对比——后者的 m_slots 数组可以包含多个 Py_mod_exec 槽,它们按出现顺序处理(为向后兼容而保留的特例)。

3.2 元数据 slots

Slot ID 值类型 说明
Py_mod_name NUL 结尾的 UTF-8 const char * 新模块的名称。模块通常经由 importlib.machinery.ModuleSpec 创建,此时会优先采用 spec 中的名称;但仍建议保留此槽以便内省与调试。3.15 新增,旧版本请使用 PyModuleDef.m_name
Py_mod_doc NUL 结尾的 UTF-8 const char * 模块的 docstring,通常设置为由 PyDoc_STRVAR 创建的变量。3.15 新增,旧版本请使用 PyModuleDef.m_doc

3.3 特性 slots

Slot ID 取值 说明
Py_mod_abi 指向 PyABIInfo 结构 描述扩展所使用的 ABI;用 PyABIInfo_VAR(abi_info); 宏定义合适变量后以 PySlot_DATA(Py_mod_abi, &abi_info) 提供。创建模块时 Python 会用 PyABIInfo_Check 校验该槽。除从 PyModuleDef 创建的模块外,此槽是必填的。3.15 新增
Py_mod_multiple_interpreters NULL / Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED / Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED / Py_MOD_PER_INTERPRETER_GIL_SUPPORTED 决定该模块能否被子解释器导入:NOT_SUPPORTED 表示不支持;SUPPORTED 表示支持,但仅限共享主解释器 GIL 的情况;PER_INTERPRETER_GIL_SUPPORTED 表示即使子解释器拥有独立 GIL 也可导入(参见 isolating-extensions-howto)。未指定时默认 Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED。3.12 新增
Py_mod_gil NULL / Py_MOD_GIL_USED / Py_MOD_GIL_NOT_USED GIL_USED 表示模块依赖 GIL 存在、可能无同步地访问全局状态;GIL_NOT_USED 表示模块在无 GIL 环境下安全。未启用 --disable-gil 的构建会忽略该槽;否则它决定导入该模块是否会自动启用 GIL。未指定时默认 Py_MOD_GIL_USED。3.13 新增,适用于 free-threaded 构建

后两个槽的取值出于历史原因被声明为指针(void *)。在 PySlot 数组中应使用 PySlot_DATA 提供:

PySlot_DATA(Py_mod_multiple_interpreters,
            Py_MOD_PER_INTERPRETER_GIL_SUPPORTED)

PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED)

这些取值宏在源码 Include/moduleobject.h 中定义,并被 Py_LIMITED_API 版本门控:Py_MOD_MULTIPLE_INTERPRETERS_* 要求 limited API 不低于 0x030c0000(3.12),Py_MOD_GIL_* 要求不低于 0x030d0000(3.13)——与文档的 versionadded 标注精确对应。

3.4 创建与初始化 slots

Slot ID 函数签名 说明
Py_mod_create PyObject *create_module(PyObject *spec, PyModuleDef *def) 创建模块对象本身的函数。spec 是“ModuleSpec-like”对象:importlib.machinery.ModuleSpec 定义的属性具有相同语义,但任意属性都可能缺失;defNULL 或(从定义创建时的)模块定义。应返回新模块对象,或设置错误并返回 NULL该函数应尽可能精简,尤其不要调用任意 Python 代码——再次导入同一模块可能引发死循环。未指定时,导入机制会用 PyModule_New 创建普通模块对象,名称取自 spec 而非定义(这样扩展模块可随符号链接在不同名称下导入而共享同一份定义)。返回对象不要求是 PyModule_Type 的实例,但 Py_mod_exec、模块状态槽(Py_mod_state_*)与 Py_mod_token 仅对 PyModule_Type 实例可用。3.5 新增;3.15 起 spec 可以是 ModuleSpec-like 对象,def 可为 NULL
Py_mod_exec int exec_module(PyObject *module) “执行/初始化”模块的函数,等价于执行 Python 模块的代码,通常用来向模块添加类与常量。3.5 新增;3.15 起禁止重复 Py_mod_exec 槽(PyModuleDef.m_slots 例外)
Py_mod_methods PyMethodDef 数组 模块级函数表,可作为 PyModule_AddFunctionsfunctions 参数。slots 数组中只允许出现一个 Py_mod_methods;若要从多个 PyMethodDef 数组添加函数,应在 Py_mod_exec 中直接调用 PyModule_AddFunctions。函数表必须静态分配(或保证比模块对象活得更久)。3.15 新增,旧版本请使用 PyModuleDef.m_methods

4. 模块状态(Module State)

扩展模块可以拥有模块状态——在模块创建时分配、模块对象释放时释放的一块内存。典型用途是保存异常类型,或模块定义的任何类型对象。模块状态通过专门的 slots 指定(见下)。

关键特性与设计理由:

  • 与模块的 Python 属性不同,Python 代码无法替换或删除存储在模块状态中的数据;
  • 把逐模块信息放在属性与模块状态中(而非静态全局变量),使模块对象相互隔离、更适合在多个子解释器中使用,也有助于解释器关闭时做有序清理;
  • 若模块状态中保存了对 Python 对象的引用,必须实现 Py_mod_state_traversePy_mod_state_clear,否则会引用泄漏。

从源码结构看(Objects/moduleobject.cassert_def_missing_or_redundant),对由 PyModuleDef 创建的模块,调试构建会断言 m_size/m_traverse/m_clear/m_free 在模块对象与静态定义之间完全一致(md_state_size == def->m_size 等),说明 CPython 在创建时把定义中的状态信息“复制”进了模块对象本身。

4.1 获取模块状态的 API

void *PyModule_GetState(PyObject *module);
/* 返回模块创建时分配的“状态”内存块指针,或 NULL(见 Py_mod_state_size)。
   出错时返回 NULL 并设置异常;用 PyErr_Occurred() 区分
   “无模块状态”与“出错”。 */

int PyModule_GetStateSize(PyObject *module, Py_ssize_t *result);
/* 3.15 新增。将模块状态的大小(由 Py_mod_state_size 或
   PyModuleDef.m_size 指定)写入 *result 并返回 0;
   出错时 *result 设为 -1,返回 -1 并设置异常。 */

4.2 定义模块状态的 slots

Slot ID 函数签名 说明
Py_mod_state_size 模块状态占用的字节数。设置为非负值表示模块可被重新初始化,并声明其所需的额外状态内存。详见 PEP 3121。3.15 新增,旧版本请使用 PyModuleDef.m_size
Py_mod_state_traverse int traverse_module_state(PyObject *module, visitproc visit, void *arg) GC 遍历模块对象时调用的遍历函数,参数含义类似 PyTypeObject.tp_traverse。若状态尚未分配(模块刚创建、Py_mod_exec 执行之前),不会调用它——精确地说,当状态大小(Py_mod_state_size)大于 0 而 PyModule_GetState() 返回 NULL 时不调用。3.15 新增,旧版本请用 PyModuleDef.m_traverse
Py_mod_state_clear int clear_module_state(PyObject *module) GC 清理模块对象时调用的清理函数。调用时机限制同上。与 PyTypeObject.tp_clear 一样,它并不总是在模块释放前被调用:例如引用计数足以判定对象不再使用时,循环 GC 不参与,会直接调用 Py_mod_state_free。3.15 新增,旧版本请用 PyModuleDef.m_clear
Py_mod_state_free int free_module_state(PyObject *module) 模块对象释放(deallocation)时调用的函数。调用时机限制同上。3.15 新增,旧版本请用 PyModuleDef.m_free

5. 模块 Token:验证“模块归属”

每个模块都可以关联一个 token:一个指针大小的值,用来标识模块状态的内存布局。当你拿到一个模块对象、但不确定它是否“属于”你的扩展时,可这样检查(文档给出的代码示例):

PyObject *module = <待检查的模块>;

void *module_token;
if (PyModule_GetToken(module, &module_token) < 0) {
    return NULL;
}
if (module_token != your_token) {
    PyErr_SetString(PyExc_ValueError, "unexpected module");
    return NULL;
}

// 该模块的状态具有预期的内存布局,可以安全地强转
struct my_state *state = (struct my_state *)PyModule_GetState(module);

模块的 token —— 也就是上例中的 your_token 取值 —— 取决于模块的创建方式:

  • PyModuleDef 创建的模块:token 是PyModuleDef 的地址
  • Py_mod_token 槽定义的模块:token 是该槽的值
  • PyModExport_* 导出钩子 创建的模块:token 是导出钩子返回的 slots 数组(除非被 Py_mod_token 覆盖)。
API 说明
Py_mod_token 3.15 新增。若用它设置 token,必须保证:该指针比模块对象活得更久,不会在他处被复用;它“属于”定义该类的扩展模块,不会与其他扩展冲突;若 token 指向某个 PyModuleDef 结构体,则模块必须表现得如同从该定义创建的一样,特别是模块状态必须有匹配的布局与语义。注意:从 PyModuleDef 创建的模块永远使用该定义结构的地址作为 token,因此 Py_mod_token 不能用在 PyModuleDef.m_slots
int PyModule_GetToken(PyObject *module, void **result) 3.15 新增。将 module 的模块 token 写入 *result 并返回 0;出错时 *result 设为 NULL,返回 -1 并设置异常

另可参考 PyType_GetModuleByToken 的反向查询。

6. 动态创建扩展模块(3.15+)

以下两个函数可用于动态创建扩展模块,而不依赖扩展自身的 export hook

PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec);
/* 3.15 新增。根据 slots 数组与 ModuleSpec 创建新模块对象。
   - slots 必须指向以 slot ID 为 0 的条目(通常写作 PySlot_END)结尾的
     PySlot 数组,且必须包含 Py_mod_abi 条目。
   - spec 可以是 Py_mod_create 文档所述的任意 ModuleSpec-like 对象;
     目前 spec 必须带有 name 属性。
   - 成功返回新模块;出错返回 NULL 并设置异常。
   - 注意:它不会处理执行槽 Py_mod_exec。
     必须同时调用 PyModule_FromSlotsAndSpec 与 PyModule_Exec
     才能完成模块的完整初始化(参见 multi-phase-initialization)。 */

int PyModule_Exec(PyObject *module);
/* 3.15 新增。执行 module 的 Py_mod_exec 槽。
   成功返回 0;出错返回 -1 并设置异常。
   若 module 没有 slots(例如使用传统单阶段初始化),
   本函数什么都不做并返回 0。 */

实现入口见 Objects/moduleobject.c 中的 PyModule_FromSlotsAndSpec(对 NULL slots 直接报错),以及 Include/moduleobject.h 中的声明——该段声明被 Py_LIMITED_API 门控在 3.15 版本(_Py_PACK_VERSION(3, 15))之后,与文档标注一致。

7. 传统方式:PyModuleDef 结构体

传统上,扩展模块用一个 module definition 作为“模块如何创建”的描述:与其直接使用 slots 数组,定义结构体为最常见功能提供了专用成员,并允许以 slots 作为扩展机制。这种方式仍然可用,且暂无移除计划。

7.1 PyModuleDef 结构体成员

struct PyModuleDef {
  PyModuleDef_Base m_base;
  const char *m_name;
  const char *m_doc;
  Py_ssize_t m_size;
  PyMethodDef *m_methods;
  PyModuleDef_Slot *m_slots;
  traverseproc m_traverse;
  inquiry m_clear;
  freefunc m_free;
};

该结构在源码 Include/moduleobject.h 中定义。文档对结构体本身的要求:必须静态分配(或以其他方式保证在任何由它创建的模块存在期间有效);通常每种这样定义的扩展模块只有一个该类型的变量。ABI 兼容性方面:该结构体(含全部成员)在非 free-threaded 构建(abi3)的 Stable ABI 中;而在 free-threaded 构建的 Stable ABI(abi3t)中,该结构体是不透明的、实际上不可用——此时应改用 slots 方式。

各成员的语义:

成员 对应 slot 说明
m_base 类型 PyModuleDef_Base,必须初始化为 PyModuleDef_HEAD_INIT。源码中(Include/moduleobject.h)可见其三个字段:m_init(仅用于支持多次初始化的遗留单阶段扩展的重初始化函数)、m_index(模块在解释器 modules_by_index 缓存中的下标,由 PyModuleDef_Init() 设置)、m_copy(遗留不可重初始化模块首次加载后 __dict__ 的副本,由 import.c 中的 fix_up_extension() 设置)。PyModuleDef_Base 同样在 abi3 Stable ABI 中、在 abi3t 中不透明
m_name Py_mod_name 模块名称
m_doc Py_mod_doc 模块文档字符串;设为 NULL 等价于省略该槽
m_size Py_mod_state_size 模块状态大小(字节)。设为 0 等价于省略该槽。使用遗留单阶段初始化或经 PyModule_Create/PyModule_Create2 动态创建模块时,m_size 可设为 -1,表示模块具有全局状态、不支持子解释器
m_methods Py_mod_methods 模块级函数表;设为 NULL 等价于省略该槽
m_slots 附加 slots 数组,以 {0, NULL} 条目结尾。注意条目使用的是较旧的 PyModuleDef_Slot 结构体(而非 PySlot)。若数组中出现了与 PyModuleDef 成员对应的槽,值必须完全一致——例如 m_slots 中用了 Py_mod_name,则 m_name 必须指向同一指针(而不只是等价的字符串)。3.5 之前该成员恒为 NULL,当时定义为 inquiry m_reload
m_traverse / m_clear / m_free Py_mod_state_traverse / Py_mod_state_clear / Py_mod_state_free 分别对应三个状态槽;设为 NULL 等价于省略。3.9 起,这三个函数不再在模块状态分配之前被调用

PyModuleDef_SlotInclude/moduleobject.h)是一个 {int slot; void *value;} 的旧式结构体,3.5 新增。文档说明了它与 PySlot 的等价转换关系:每个 PyModuleDef_Slot 条目 modslot 被解释为

(PySlot){
   .sl_id    = modslot.slot,
   .sl_flags = PySlot_INTPTR | sub_static,
   .sl_ptr   = modslot.value
}

其中 sub_static 在槽要求静态标志(如 Py_mod_methods)时、或(若存在)父级 Py_mod_slots 槽带有该标志时,取 PySlot_STATIC。两个方向都可以互相嵌套:PyModuleDef_Slot 数组可经 Py_mod_slots 装入 PySlot 数组,反之 PySlot 子数组可经 Py_mod_slot_subslots(文档中写作 Py_mod_slots/PySlot 互转机制,即 Py_mod_slotsPy_slot_subslots 两个槽 ID)装入。

相关的类型与槽常量:

  • PyTypeObject PyModuleDef_TypePyModuleDef 对象的类型(源码见 Objects/moduleobject.ctp_name"moduledef");
  • Py_mod_slots(3.15 新增):作用类似 PySlot_SUBSLOTS,但声明的是 PyModuleDef_Slot 结构体数组。

7.2 从 PyModuleDef 创建模块的 API

函数 说明
PyObject *PyModule_Create(PyModuleDef *def) 根据 def 创建新模块对象。这是一个宏,转调 PyModule_Create2module_api_versionPYTHON_API_VERSION;使用 limited API 时取 PYTHON_ABI_VERSION
PyObject *PyModule_Create2(PyModuleDef *def, int module_api_version) 同上,但假定 API 版本为 module_api_version;若版本与运行解释器不符,会发出 RuntimeWarning。出错返回 NULL 并设置异常。不支持 slotsdefm_slots 成员必须为 NULL。文档提示:绝大多数场景应改用 PyModule_Create,确定需要时再用它
PyObject *PyModule_FromDefAndSpec(PyModuleDef *def, PyObject *spec) 宏,转调 PyModule_FromDefAndSpec2,版本参数同上。3.5 新增。软弃用:新代码建议改用 PyModule_FromSlotsAndSpec
PyObject *PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version) 根据 def 与 ModuleSpec spec 创建新模块,版本不匹配时发 RuntimeWarning;出错返回 NULL 并设置异常。注意它不处理执行槽Py_mod_exec),必须同时调用 PyModule_FromDefAndSpecPyModule_ExecDef 才能完成初始化。3.5 新增。软弃用:新代码建议改用 PyModule_FromSlotsAndSpec
int PyModule_ExecDef(PyObject *module, PyModuleDef *def) 处理 def 中声明的执行槽(Py_mod_exec)。3.5 新增。软弃用:要执行模块自身的执行槽,建议改用 PyModule_Exec,因为它同样适用于未从 PyModuleDef 创建的模块

版本常量:

  • PYTHON_API_VERSION / PYTHON_API_STRING:C API 版本,整型(1013)与字符串("1013")。仅为向后兼容而定义;当前常量在新 Python 版本中不再更新,不能用于版本判断,未来可能改变;
  • PYTHON_ABI_VERSION / PYTHON_ABI_STRING:定义为 3"3",情况同上。

源码侧的印证:Objects/moduleobject.c 中的 check_api_version() 正是“版本不匹配则发 RuntimeWarning”的实现——它比较 module_api_versionPYTHON_API_VERSION/PYTHON_ABI_VERSION,不匹配时用 PyErr_WarnFormat(PyExc_RuntimeWarning, 1, ...) 发出“Python C API version mismatch for module ...”警告;而 Objects/moduleobject.cPyModule_Create2 会先检查导入机制是否已初始化,未初始化时抛出 SystemError("Python import machinery not initialized"),再转调内部函数 _PyModule_CreateInitialized()(该内部函数在检测到 m_slots 非空时抛出 SystemError,对应文档“不支持 slots”的约束)。

8. 支持函数(模块初始化辅助)

以下函数用于帮助初始化模块对象,适用于模块的执行槽(Py_mod_exec)、遗留单阶段初始化的初始化函数,或动态创建模块的代码。

8.1 添加对象:AddObjectRef / Add / AddObject

三个函数语义相近,区别在于引用计数归属,这是最容易写出引用泄漏的地方:

函数 引用语义 版本
int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) 把对象以 name 加入 module。成功返回 0;出错抛异常返回 -1。不偷引用:调用方保留所有权。宽容地接受“已设置异常的 NULL 值”——此时返回 -1 且不改动已抛出的异常。3.10 新增
int PyModule_Add(PyObject *module, const char *name, PyObject *value) 类似 PyModule_AddObjectRef,但偷取 value 的引用(即使出错也偷)。可以直接把返回新引用的函数结果传进去,无需检查或暂存。3.13 新增
int PyModule_AddObject(PyObject *module, const char *name, PyObject *value) 类似 PyModule_AddObjectRef,但仅在成功时偷取引用。文档明确建议改用 PyModule_AddPyModule_AddObjectRef,因为 PyModule_AddObject 极易误用造成泄漏;3.13 起被软弃用

文档给出的示例代码:

/* PyModule_AddObjectRef:不偷引用,需自行释放 */
static int
add_spam(PyObject *module, int value)
{
    PyObject *obj = PyLong_FromLong(value);
    if (obj == NULL) {
        return -1;
    }
    int res = PyModule_AddObjectRef(module, "spam", obj);
    Py_DECREF(obj);
    return res;
}

/* 等价写法:利用“接受带异常的 NULL”省去显式判空,
   注意此处必须用 Py_XDECREF(),因为 obj 可能是 NULL */
static int
add_spam(PyObject *module, int value)
{
    PyObject *obj = PyLong_FromLong(value);
    int res = PyModule_AddObjectRef(module, "spam", obj);
    Py_XDECREF(obj);
    return res;
}
/* PyModule_Add:偷引用(即使出错也偷),可直接链式调用 */
if (PyModule_Add(module, "spam", PyBytes_FromString(value)) < 0) {
    goto error;
}
/* PyModule_AddObject:仅成功时偷引用,失败时必须手动释放 */
PyObject *obj = PyBytes_FromString(value);
if (PyModule_AddObject(module, "spam", obj) < 0) {
    /* 'obj' 非 NULL 且失败时,必须用 Py_XDECREF() 删除
       'obj' 的强引用;'obj' 为 NULL 时 Py_XDECREF() 是空操作 */
    Py_XDECREF(obj);
    goto error;
}
/* 成功路径上 PyModule_AddObject() 已偷走 obj 的引用:
   此处无需 Py_XDECREF(obj) */

补充约束:传给这些函数的不同 name 字符串数量应尽量少,通常只用静态分配的字符串作为 name;对运行时才确定的名称,建议直接调用 PyUnicode_FromStringPyObject_SetAttr。更多细节参见 PyUnicode_InternFromString(它可能被内部用于创建键对象)。

8.2 常量与类型

API 说明
int PyModule_AddIntConstant(PyObject *module, const char *name, long value) namemodule 添加整型常量,可在模块初始化函数中使用。出错返回 -1 并设异常,成功返回 0。内部依次调用 PyLong_FromLongPyModule_AddObjectRef
int PyModule_AddStringConstant(PyObject *module, const char *name, const char *value) 添加字符串常量,value 必须 NUL 结尾。内部调用 PyUnicode_InternFromStringPyModule_AddObjectRef
PyModule_AddIntMacro(module, macro) 宏:以 macro 的名称与值添加整型常量,例如 PyModule_AddIntMacro(module, AF_INET) 会以 AF_INET 之名、AF_INET 之值加入 module
PyModule_AddStringMacro(module, macro) 宏:以宏名与宏值添加字符串常量
int PyModule_AddType(PyObject *module, PyTypeObject *type) module 添加类型对象;内部调用 PyType_Ready 完成类型初始化;类型名取自 tp_name 最后一个 . 之后的分量。3.9 新增

8.3 函数表与 docstring

函数 说明
int PyModule_AddFunctions(PyObject *module, PyMethodDef *functions) 把以 NULL 结尾的 functions 数组中的函数加入 module。由于 C 模块级函数没有共享的模块命名空间,C 实现的模块级“函数”通常把模块对象作为第一个参数接收(类似 Python 类的实例方法)。从 PyModuleDef 创建模块时(如 multi-phase-initialization、PyModule_CreatePyModule_FromDefAndSpec)本函数会被自动调用;若作者偏好把函数分散在多个 PyMethodDef 数组中定义,则应直接调用它。functions 数组必须静态分配(或保证比模块对象活得久)。3.5 新增
int PyModule_SetDocString(PyObject *module, const char *docstring) 设置 module 的 docstring。从 PyModuleDef 创建模块时同样会被自动调用。成功返回 0,出错返回 -1 并设异常。3.5 新增
int PyUnstable_Module_SetGIL(PyObject *module, void *gil) Py_mod_gil 的取值声明 module 是否支持无 GIL 运行;在使用单阶段初始化时,必须在模块的初始化函数中调用。若初始化期间未调用,导入机制假定模块不支持无 GIL 运行。仅在启用 --disable-gil 的 Python 构建中可用。3.13 新增

源码印证:PyModule_AddFunctions 的底层 _add_methods_to_object()Objects/moduleobject.c)会拒绝 METH_CLASS/METH_STATIC 标志(抛 ValueError: module functions cannot set METH_CLASS or METH_STATIC),并用 PyCFunction_NewEx(fdef, (PyObject*)module, name) 把模块对象绑定为函数的 self 参数——正对应文档“模块级函数以模块为第一参数”的描述。

9. 模块查找(单阶段初始化)

遗留的单阶段初始化方案创建的是单例模块,可在当前解释器上下文中查找:只需模块定义的引用,就能稍后取回模块对象。但注意:这些函数对多阶段初始化创建的模块无效,因为同一份定义可以创建多个此类模块。

函数 说明
PyObject *PyState_FindModule(PyModuleDef *def) 返回为当前解释器由 def 创建的模块对象。要求此前已用 PyState_AddModule 把模块对象挂到解释器状态上;找不到或尚未挂载时返回 NULL
int PyState_AddModule(PyObject *module, PyModuleDef *def) 把模块对象挂到解释器状态,使其可被 PyState_FindModule 访问。仅对单阶段初始化的模块有效。Python 在导入单阶段初始化模块后会自动调用 PyState_AddModule,因此模块初始化代码中无需(但无害地)调用;只有当模块自身的初始化代码随后要调用 PyState_FindModule 时才需要显式调用。该函数主要面向实现替代导入机制的场景(直接调用,或参考其实现了解所需的状态更新细节)。若此前用同一 def 挂过模块,则被新 module 替换。调用方必须拥有已附加的线程状态(attached thread state)。出错返回 -1 并设异常,成功返回 0。3.3 新增
int PyState_RemoveModule(PyModuleDef *def) 从解释器状态中移除由 def 创建的模块对象。出错返回 -1 并设异常,成功返回 0。调用方必须拥有已附加的线程状态。3.3 新增

10. 选型建议与版本速查

如何为扩展模块选择定义方式(综合本文各节约束):

  • 需要同时兼容旧版 Python,或目标构建使用非 free-threaded Stable ABI(abi3):使用传统的 PyModuleDefPyModule_Create / PyModule_FromDefAndSpec + PyModule_ExecDef),因为 PyModuleDefPyModuleDef_Base 在该 ABI 中可用;
  • 目标为较新的 CPython(3.15+,尤其 free-threaded abi3t 构建,PyModuleDef 在该 ABI 中不透明):使用 PySlot 数组(PyModule_FromSlotsAndSpec + PyModule_Exec),并记得必填 Py_mod_abi 槽;
  • 多阶段初始化场景下无论哪种方式,From*Exec* 都必须成对调用,模块才算完整初始化。

API 版本速查(依据文档标注与源码中的 Py_LIMITED_API 门控):

API 新增版本
PyModule_GetFilenameObjectPyState_AddModulePyState_RemoveModule 3.2(PyModule_GetFilename 同期弃用)
PyModule_NewObjectPyModule_GetNameObject 3.3
Py_mod_create/Py_mod_execPyModule_AddFunctionsPyModule_SetDocStringPyModuleDef.m_slotsPyModuleDef_SlotPyModule_FromDefAndSpec(2)PyModule_ExecDef 3.5
PyModule_AddObjectRef 3.10
Py_mod_multiple_interpreters 3.12
Py_mod_gilPyUnstable_Module_SetGILPyModule_Add 3.13
slots 定义方式(Py_mod_name/Py_mod_doc/Py_mod_abi/Py_mod_methods/Py_mod_state_*/Py_mod_token/Py_mod_slots)、PyModule_GetStateSizePyModule_GetTokenPyModule_FromSlotsAndSpecPyModule_Exec 3.15
PyModule_AddObject 软弃用(3.13 起建议改用 PyModule_Add/PyModule_AddObjectRef);PyModule_FromDefAndSpec(2)PyModule_ExecDef 同样建议改用 slots 版本

参考文件(均相对仓库根目录):

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
903
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341