Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python增强提案

PEP 3121 – 扩展模块初始化和终结

作者:
Martin von Löwis <martin at v.loewis.de>
状态:
最终
类型:
标准跟踪
创建:
2007年4月27日
Python版本:
3.0
历史记录:


目录

重要

此PEP是一个历史文档。最新的规范文档现在可以在PyInit_modulename()PyModuleDef中找到。

×

请参阅PEP 1了解如何提出更改。

摘要

扩展模块初始化目前存在一些缺陷。没有模块清理功能,入口点名称可能会导致命名冲突,入口函数不遵循通常的调用约定,并且不支持多个解释器。此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