PEP 3121 – 扩展模块初始化和终结
- 作者:
- Martin von Löwis <martin at v.loewis.de>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2007年4月27日
- Python版本:
- 3.0
- 历史记录:
摘要
扩展模块初始化目前存在一些缺陷。没有模块清理功能,入口点名称可能会导致命名冲突,入口函数不遵循通常的调用约定,并且不支持多个解释器。此PEP解决了这些问题。
问题
模块终结
目前,扩展模块通常只初始化一次,然后“永久存在”。唯一的例外是调用Py_Finalize()时:然后初始化例程会再次被调用。从资源管理的角度来看,这很糟糕:每次调用初始化时都可能会分配内存和其他资源,但没有办法回收它们。因此,目前没有办法完全释放Python分配的所有资源。
入口点名称冲突
入口点目前称为init<module>。这可能会与其他也称为init<something>的符号冲突。特别是,已知initsocket在过去曾发生过冲突(此特定问题作为将模块重命名为_socket的副作用而得到解决)。
入口点签名
入口点目前是一个过程(返回void)。这偏离了通常的调用约定;调用者只能通过检查PyErr_Occurred来了解初始化期间是否存在错误。入口点应返回一个PyObject*,该对象将是创建的模块,或者在发生异常时返回NULL。
多个解释器
目前,扩展模块在所有解释器之间共享其状态。这可能导致解释器之间出现不希望的信息泄漏:一个脚本可能会永久损坏扩展模块中的对象,从而可能破坏其他解释器中的所有脚本。
规范
模块初始化例程将其签名更改为
PyObject *PyInit_<modulename>()
每次导入模块时,初始化例程将在每个解释器上调用一次。它应该每次都返回一个新的模块对象。
为了在C变量中存储每个模块的状态,每个模块对象都将包含一块内存,该内存仅由模块解释。在创建模块时指定用于模块的内存量。
除了初始化函数之外,模块还可以实现许多其他回调函数,这些函数在调用模块的tp_traverse、tp_clear和tp_free函数以及重新加载模块时调用。
整个模块定义组合在一个PyModuleDef结构体中
struct PyModuleDef{
PyModuleDef_Base m_base; /* To be filled out by the interpreter */
Py_ssize_t m_size; /* Size of per-module data */
PyMethodDef *m_methods;
inquiry m_reload;
traverseproc m_traverse;
inquiry m_clear;
freefunc m_free;
};
模块的创建更改为期望一个可选的PyModuleDef*。模块状态将被初始化为null。
每个模块方法都将模块对象作为第一个参数传递。要访问模块数据,将提供一个函数
void* PyModule_GetState(PyObject*);
。此外,为了比遍历sys.modules更有效地查找模块,将提供一个函数
PyObject* PyState_FindModule(struct PyModuleDef*);
。此查找函数将使用位于m_base字段中的索引,通过索引而不是名称查找模块。
由于所有Python对象都应通过Python内存管理进行控制,因此不鼓励使用“静态”类型对象,除非类型对象本身没有内存管理状态。为了简化堆类型的定义,添加了一个新方法
PyTypeObject* PyType_Copy(PyTypeObject*);
。
示例
xxmodule.c将更改为删除initxx函数,并添加以下代码
struct xxstate{
PyObject *ErrorObject;
PyObject *Xxo_Type;
};
#define xxstate(o) ((struct xxstate*)PyModule_GetState(o))
static int xx_traverse(PyObject *m, visitproc v,
void *arg)
{
Py_VISIT(xxstate(m)->ErrorObject);
Py_VISIT(xxstate(m)->Xxo_Type);
return 0;
}
static int xx_clear(PyObject *m)
{
Py_CLEAR(xxstate(m)->ErrorObject);
Py_CLEAR(xxstate(m)->Xxo_Type);
return 0;
}
static struct PyModuleDef xxmodule = {
{}, /* m_base */
sizeof(struct xxstate),
&xx_methods,
0, /* m_reload */
xx_traverse,
xx_clear,
0, /* m_free - not needed, since all is done in m_clear */
}
PyObject*
PyInit_xx()
{
PyObject *res = PyModule_New("xx", &xxmodule);
if (!res) return NULL;
xxstate(res)->ErrorObject = PyErr_NewException("xx.error", NULL, NULL);
if (!xxstate(res)->ErrorObject) {
Py_DECREF(res);
return NULL;
}
xxstate(res)->XxoType = PyType_Copy(&Xxo_Type);
if (!xxstate(res)->Xxo_Type) {
Py_DECREF(res);
return NULL;
}
return res;
}
讨论
Tim Peters在[1]中报告说,PythonLabs曾经考虑过这样的功能,并列出了此PEP中目前不支持的其他一些钩子
- 当模块对象从sys.modules中删除时
- 当调用Py_Finalize时
- 当Python退出时
- 当Python DLL卸载时(仅限Windows)
参考文献
版权
此文档已归入公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3121.rst
上次修改时间:2023年10月11日09:31:29 GMT