若是你知道如何用C语言编程,那么为Python添加新的内置模块是很容易的。这种扩展模块能够作两件不能直接在Python中完成的事情:它们能够实现新的内置对象类型,以及调用C库函数和系统调用。html
为了支持扩展,Python API(应用程序员接口)定义了一组函数、宏和变量,它们提供对Python运行时系统大部分方面的访问。Python API经过包含头文件"Python.h"被合并到C源文件中。python
扩展模块的编译取决于其预期用途以及系统设置; 细节在后面的章节中给出。程序员
注意:C扩展接口是CPython特有的,扩展模块不适用于其余Python实现。在许多状况下,能够避免编写C扩展并保持其余实现的可移植性。例如,若是您的用例调用C库函数或系统调用,则应考虑使用ctypes模块或cffi库,而不是编写自定义C代码。这些模块容许您编写Python代码以与C代码交互,而且在编写和编译C扩展模块时,它们在Python的实现之间更具可移植性。shell
让咱们建立一个名为spam(Monty Python粉丝最喜欢的食物...)的扩展模块,并假设咱们要为C库函数system()建立一个Python接口。该函数将一个以空字符结尾的字符串做为参数,并返回一个整数。咱们但愿这个函数能够从Python中调用,以下所示:编程
>>> import spam >>> status = spam.system("ls -l")
首先建立一个文件spammodule.c(根据历史经验,若是一个模块名称为spam,那么包含其实现的C文件名称为spammodule.c;若是模块名称很长,好比spammify,那么模块名称能够是spammify.c)windows
咱们文件的第一行是:api
#include <Python.h>
它引入了Python API(若是你喜欢,你能够添加一个描述模块用途的注释和一个版权声明)。数组
注意:因为Python可能会定义一些影响某些系统上标准头文件的预处理器定义,所以必须在包含Python.h任何标准头文件以前包含这些定义。缓存
由Python.h所定义的全部用户可见符号都具备前缀Py或PY,除了在标准头文件中定义的符号。为了方便起见,"Python.h" 包括一些被Python解释普遍使用的标准头文件:<stdio.h>、<string.h>、 <errno.h>和<stdlib.h>
。若是是后者的头文件没有您的系统上存在,它将直接声明malloc(),free()和 realloc()函数。安全
接下来咱们添加C语言函数到模块文件中,在使用Python表达式spam.system(string)时被调用(咱们将很快看到它是如何被调用的):
static PyObject * spam_system(PyObject *self, PyObject *args) { const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = system(command); return PyLong_FromLong(sts); }
Python中的参数列表和C函数的参数列表之间有一个简单的映射关系(例如单个表达式"ls -l")。C函数老是有两个参数,一般命名为self和args。
对于模块级别的函数self变量指向模块对象,对于对象方法self变量指向对象实例。
ARGS参数指向包含参数的Python元组对象。元组中的每一项都对应于调用参数列表中的参数。参数是Python对象 - 为了在C函数中对它们作任何事情,咱们必须将它们转换为C值。Python API中的PyArg_ParseTuple()函数用于参数类型检查并将它们转换为C值。它使用模板字符串来肯定参数的所需类型以及存储转换值的C变量的类型,稍后再详细介绍。
若是全部参数都具备正确的类型而且被存储到传递的变量地址中,则PyArg_ParseTuple()返回true(非零)。若是传递了一个无效参数列表,它将返回false(零)。在后一种状况下,它也会引起适当的异常,因此调用函数能够当即返回NULL(如咱们在示例中所见)。
整个Python解释器中一个重要的约定以下:当一个函数失败时,它应该设置一个异常条件并返回一个错误值(一般是一个NULL指针)。异常存储在解释器内的静态全局变量中,若是此变量为NULL,则不会发生异常。第二个全局变量存储异常的"关联值"(第二个参数raise)。第三个变量包含栈回溯,以防错误发生在Python代码中。这三个变量是Python中sys.exc_info()执行结果的C等价物(请参阅sysPython库参考中模块部分)。了解他们了解错误是如何传递的很是重要。
Python API定义了许多函数来设置各类类型的异常。
最多见的是PyErr_SetString()。它的参数是一个异常对象和一个C字符串:异常对象一般是一个预约义的对象例如PyExc_ZeroDivisionError;C字符串指示错误的缘由,并转换为Python字符串对象存入异常的"关联值"。
另外一个有用的函数是PyErr_SetFromErrno(),它只接受一个异常参数,并经过检查全局变量来构造关联的值errno。最通用的函数是PyErr_SetObject(),它接受两个对象参数,异常及其相关的值。您不须要对传递给这些函数的对象执行Py_INCREF()。
经过调用PyErr_Occurred()您能够非破坏性地测试是否设置了例外,它将返回当前的异常对象, 若是没有发生异常,则返回NULL。一般您不须要调用PyErr_Occurred()以查看函数调用是否发生错误,由于您应该可以根据返回值进行分析。
当调用另外一个函数g的函数f检测到后者失败时,f自己应该返回一个错误值(一般为NULL或-1)。它不该该再调用任何PyErr_*()
函数 - 由于g已经调用了。f的调用者应该也返回一个错误指示它的调用者,一样不须要再调用任何PyErr_*()
函数,而后继续 - 错误的最详细缘由已经由首次检测到它的函数报告(例如此处的g函数)。一旦错误到达Python解释器的主循环,就会停止当前正在执行的Python代码,并尝试查找由Python程序员指定的异常处理程序。
(有些状况下模块实际上能够经过调用另外一个PyErr_*()
函数来提供更详细的错误消息,在这种状况下能够这样作,但一般状况下,这不是必需的,而且能够致使有关缘由的信息的错误将会丢失:大多数操做可能因为各类缘由而失败。)
要忽略由失败的函数调用设置的异常,必须经过调用PyErr_Clear()明确地清除异常状况。C代码调用PyErr_Clear()仅当它不想将错误传递给解释器,但但愿彻底由它本身处理(可能经过尝试别的东西,或伪装没有出错)。
每次失败的malloc()调用都必须变成异常 - malloc()/realloc()的直接调用者必须本身调用PyErr_NoMemory()并返回失败指示符。全部的对象建立函数(例如PyLong_FromLong())都已经这样作了,因此这个注释只与那些malloc()直接调用者有关。
还要注意的是,除了PyArg_ParseTuple()函数以外,返回整数状态的函数一般返回正值或零值表示成功,-1表示失败,如Unix系统调用。
最后,当你返回一个错误指示符时,要注意垃圾回收(经过向你已经建立的对象发出Py_XDECREF()或Py_DECREF()调用)!
选择哪一个异常来彻底取决于你。有预先声明的C对象与全部内置的Python异常相对应,好比 PyExc_ZeroDivisionError,您能够直接使用它,但你应该明智地选择异常 - 不要用PyExc_TypeError来表示文件没法打开(应该可能PyExc_IOError)。若是参数列表有问题,PyArg_ParseTuple()函数一般会引起PyExc_TypeError异常。若是须要表示一个其值必须在一个特定的范围或必须知足其余条件的异常,PyExc_ValueError是适当的。
您也能够为模块定义一个新的异常,一般您须要在文件的开头声明一个静态对象变量:
static PyObject *SpamError;
并在模块的初始化函数(PyInit_spam())中使用一个异常对象初始化它(如今先忽略错误检查部分):
PyMODINIT_FUNC PyInit_spam(void) { PyObject *m; m = PyModule_Create(&spammodule); if (m == NULL) return NULL; SpamError = PyErr_NewException("spam.error", NULL, NULL); Py_INCREF(SpamError); PyModule_AddObject(m, "error", SpamError); return m; }
请注意,异常对象的Python名称是spam.error。该 PyErr_NewException()函数能够建立一个基类为Exception的类(除非传入另外一个类而不是NULL),如内置异常中所述。
还要注意,该SpamError变量保留对新建立的异常类的引用,这是故意的!因为能够经过外部代码从模块中删除异常,因此须要拥有该类的引用,来确保SpamError不会由于被丢弃,从而成为悬挂指针。若是它成为悬挂指针,引起异常的C代码可能会致使崩溃或其余意外反作用。
稍后在本示例中咱们将讨论PyMODINIT_FUNC
做为函数返回类型的用法。
在你的扩展模块中,能够经过调用PyErr_SetString()来引起spam.error异常,以下所示:
static PyObject * spam_system(PyObject *self, PyObject *args) { const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = system(command); if (sts < 0) { PyErr_SetString(SpamError, "System command failed"); return NULL; } return PyLong_FromLong(sts); }
回到咱们的示例函数,您如今应该可以理解这个语句:
if (!PyArg_ParseTuple(args, "s", &command)) return NULL;
若是在参数列表中检测到错误,则返回NULL(函数返回对象指针的错误指示符),依赖于PyArg_ParseTuple()设置的异常 。不然参数的字符串值已被复制到局部变量command。这是一个分配的指针,你不该该修改它指向的字符串(因此在标准C中,变量command应该被正确地声明为const char *command
)。
下一个语句是对Unix函数system()的调用,并传递给它咱们刚刚从PyArg_ParseTuple()获得的字符串:
sts = system(command);
咱们的spam.system()函数必须将sts的值做为Python对象返回,经过调用PyLong_FromLong()函数完成。
return PyLong_FromLong(sts);
在这种状况下,它将返回一个整数对象。(是的,甚至整数都是Python中的堆对象!)
若是你有一个没有返回值的C函数(函数返回值为void),那么相应的Python函数必须返回None。你须要这么作(这是由Py_RETURN_NONE宏实现的):
Py_INCREF(Py_None); return Py_None;
Py_None是Python对象None的C名称。正如咱们所看到的,它是一个真正的Python对象而不是NULL指针,这在大多数状况下都意味着"错误"。
我承诺展现如何从Python程序中调用spam_system()。首先,咱们须要在"方法表"中列出其名称和地址:
static PyMethodDef SpamMethods[] = { ... {"system", spam_system, METH_VARARGS, "Execute a shell command."}, ... {NULL, NULL, 0, NULL} /* Sentinel */ };
请注意第三项(METH_VARARGS)。这是一个标志,告诉解释器用于C函数的调用约定。它一般应该是METH_VARARGS
或METH_VARARGS | METH_KEYWORDS
。
仅使用METH_VARARGS
时,函数指望将Python级的参数做为可以被PyArg_ParseTuple()解析的元组传递进来,关于这个功能的更多信息在下面提供。
若是关键字参数应该传递给函数,那么METH_KEYWORDS
位将被设置。在这种状况下,C函数应该接受第三个参数,该参数将成为关键字字典,使用相似PyObject *PyArg_ParseTupleAndKeywords()
的函数来解析参数。
方法表必须在模块定义结构中被引用:
static struct PyModuleDef spammodule = { PyModuleDef_HEAD_INIT, "spam", /* name of module */ spam_doc, /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ SpamMethods };
这个结构又必须在模块的初始化函数中传递给解释器,初始化函数必须被命名PyInit_name(),其中name是模块的名称,而且应该是模块文件中惟必定义的非static项目:
PyMODINIT_FUNC PyInit_spam(void) { return PyModule_Create(&spammodule); }
请注意,PyMODINIT_FUNC
声明该函数的返回类型为PyObject *
,声明平台所需的任何特殊连接声明,而且声明C++函数为extern "C"
。
当Python程序第一次导入spam模块时, PyInit_spam()被调用。它调用PyModule_Create()返回模块对象,并根据PyMethodDef模块定义中的表(结构数组)插入内置函数对象到新建立的模块中。 PyModule_Create()返回一个指向它建立的模块对象的指针。若是因致命错误而停止或者模块初始化失败,则返回NULL。init函数必须将模块对象返回给其调用者,以便将其插入sys.modules
。
嵌入Python时,除非PyImport_Inittab表中有对应条目,不然不会自动调用PyInit_spam()函数。要将模块添加到初始化表中,请使用PyImport_AppendInittab()(可选),而后导入模块:
int main(int argc, char *argv[]) { wchar_t *program = Py_DecodeLocale(argv[0], NULL); if (program == NULL) { fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); exit(1); } /* Add a built-in module, before Py_Initialize */ PyImport_AppendInittab("spam", PyInit_spam); /* Pass argv[0] to the Python interpreter */ Py_SetProgramName(program); /* Initialize the Python interpreter. Required. */ Py_Initialize(); /* Optionally import the module; alternatively, import can be deferred until the embedded script imports it. */ PyImport_ImportModule("spam"); ... PyMem_RawFree(program); return 0; }
注意:从sys.modules删除条目或将编译的模块导入进程中的多个解释器(或者在fork()没有插入的状况下执行exec())会致使某些扩展模块出现问题。扩展模块做者在初始化内部数据结构时应该谨慎行事。
Python源代码分发中包含了一个更实质性的示例模块Modules/xxmodule.c。这个文件能够做为模板使用,或者只是做为一个例子阅读。
注意 与咱们的spam示例不一样,xxmodule它使用多阶段初始化 (Python 3.5中的新增功能),从PyInit_spam中返回PyModuleDef结构 ,而且将模块建立留给导入机制。
在使用新扩展以前,还有两件事要作:编译并将其与Python系统连接。若是使用动态加载,细节可能取决于系统使用的动态加载样式,查阅Building C and C++ Extensions以及Building C and C++ Extensions on Windows获取更多信息。
若是你不能使用动态加载,或者若是你想让你的模块成为Python解释器的一个永久部分,你将不得不改变配置设置并重建解释器。幸运的是,在Unix上这很是简单:只需将您的文件(spammodule.c例如)放入Modules/解压源代码发行版的目录中,而后在Modules/Setup.local
描述文件的文件中添加一行 :
spam spammodule.o
并经过在顶级目录中运行make来重建解释器。您也能够在Modules/
子目录中运行make,可是必须先经过运行make Makefile
来从新编译Makefile。(每次更改Setup文件时都须要这样作)
若是您的模块须要额外的库进行连接,这些库也能够在配置文件的行中列出,例如:
spam spammodule.o -lX11
到目前为止,咱们已经集中在使C函数能够从Python调用。反过来也颇有用:从C中调用Python函数。对于支持所谓的"回调"函数的库尤为如此。若是C接口使用回调函数,等效的Python一般须要为Python程序员提供回调机制,该实现将须要从C回调调用Python回调函数。其余用途也是能够想象的。
幸运的是,Python解释器很容易被递归调用,而且有一个标准的接口来调用Python函数。(我不会详细讨论如何使用特定的字符串做为输入来调用Python解析器 - 若是您有兴趣,请查看Python源代码中的-c命令行选项的实现Modules/main.c)
调用Python函数很容易。首先,Python程序必须以某种方式将Python函数对象传递给你。您应该提供一个功能(或其余一些界面)来完成此操做。当这个函数被调用时,将一个指向Python函数对象的指针(注意Py_INCREF()它!)保存在全局变量中 - 或者任何你认为合适的地方。例如,如下函数多是模块定义的一部分:
static PyObject *my_callback = NULL; static PyObject * my_set_callback(PyObject *dummy, PyObject *args) { PyObject *result = NULL; PyObject *temp; if (PyArg_ParseTuple(args, "O:set_callback", &temp)) { if (!PyCallable_Check(temp)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_XINCREF(temp); /* Add a reference to new callback */ Py_XDECREF(my_callback); /* Dispose of previous callback */ my_callback = temp; /* Remember new callback */ /* Boilerplate to return "None" */ Py_INCREF(Py_None); result = Py_None; } return result; }
该功能必须使用METH_VARARGS
标志向解释器注册,这在The Module’s Method Table and Initialization Function一节中有描述。PyArg_ParseTuple()函数及其参数说明记录在Extracting Parameters in Extension Functions。
宏Py_XINCREF()和Py_XDECREF()用来增长/减小一个对象的引用计数,即便是NULL指针也是安全的(但请注意,在这种状况下temp不会为NULL)。有关它们的更多信息,请参阅参考计数部分。
稍后,当须要调用该函数的时候,您可使用C函数PyObject_CallObject()。这个函数有两个参数,都是指向任意Python对象的指针:Python函数和参数列表。参数列表必须始终是一个元组对象,其长度是参数个数。调用没有参数的Python函数时,传入NULL或空元组; 调用一个参数的Python函数时,传递一个单例元组。当其格式字符串由零或更多格式代码组成时,Py_BuildValue()返回一个元组。例如:
int arg; PyObject *arglist; PyObject *result; ... arg = 123; ... /* Time to call the callback */ arglist = Py_BuildValue("(i)", arg); result = PyObject_CallObject(my_callback, arglist); Py_DECREF(arglist);
PyObject_CallObject()返回一个Python对象指针:这是Python函数的返回值。在这个例子中,建立了一个新的元组做为参数列表,该列表在调用PyObject_CallObject()后经过Py_DECREF调用被当即释放。
返回值PyObject_CallObject()是"new":它是一个全新的对象或者它是引用计数已递增的现有对象,因此除非你想把它保存在一个全局变量中,不然你应该以某种方式对获得的结果执行Py_DECREF(),甚至(尤为是!)若是你对它的价值不感兴趣。
可是,在执行此操做以前,检查返回值是否非空很是重要。若是为空,则经过引起异常终止Python函数。若是是从Python调用的C代码PyObject_CallObject(),应该向其Python调用者返回错误指示,以便解释器能够打印堆栈跟踪或者调用能够处理异常的Python代码。若是这不可能或不可取,应经过调用清除异常PyErr_Clear()。例如:
if (result == NULL) return NULL; /* Pass error back */ ...use result... Py_DECREF(result);
根据Python回调函数接口,您可能还须要提供参数列表给PyObject_CallObject()。您可能须要构造一个新的元组做为参数列表,最简单的方法就是调用Py_BuildValue()。例如您想传递一个整型事件编码,则可使用如下代码:
PyObject *arglist; ... arglist = Py_BuildValue("(l)", eventcode); result = PyObject_CallObject(my_callback, arglist); Py_DECREF(arglist); if (result == NULL) return NULL; /* Pass error back */ /* Here maybe use the result */ Py_DECREF(result);
注意Py_DECREF(arglist)的位置,必须在调用以后,错误检查以前!还要注意,严格来讲这个代码不完整:Py_BuildValue()可能会耗尽内存,须要进行检查。
您也能够经过使用支持参数和关键字参数的PyObject_Call()来调用函数。正如在上面的例子中,咱们Py_BuildValue()用来构造字典。
PyObject *dict; ... dict = Py_BuildValue("{s:i}", "name", val); result = PyObject_Call(my_callback, NULL, dict); Py_DECREF(dict); if (result == NULL) return NULL; /* Pass error back */ /* Here maybe use the result */ Py_DECREF(result);
PyArg_ParseTuple()函数声明以下:
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
ARG参数必须是包含Python传递给C函数的参数列表的元组对象。format参数必须是一个格式字符串,其语法在Python/C API参考手册Parsing arguments and building values中给出。其他的参数必须是变量的地址,其类型由格式字符串决定。
请注意,虽然PyArg_ParseTuple()检查Python参数是否具备所需的类型,但它不能检查传递给调用的C变量地址的有效性:若是在那里犯错,您的代码可能会崩溃或至少覆盖内存中的随机位。因此要当心!
请注意,提供给调用者的任何Python对象引用都是借用引用,不要减小他们的参考计数!
一些示例调用:
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */ #include <Python.h> int ok; int i, j; long k, l; const char *s; Py_ssize_t size; ok = PyArg_ParseTuple(args, ""); /* No arguments */ /* Python call: f() */ ok = PyArg_ParseTuple(args, "s", &s); /* A string */ /* Possible Python call: f('whoops!') */ ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */ /* Possible Python call: f(1, 2, 'three') */ ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size); /* A pair of ints and a string, whose size is also returned */ /* Possible Python call: f((1, 2), 'three') */ { const char *file; const char *mode = "r"; int bufsize = 0; ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize); /* A string, and optionally another string and an integer */ /* Possible Python calls: f('spam') f('spam', 'w') f('spam', 'wb', 100000) */ } { int left, top, right, bottom, h, v; ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)", &left, &top, &right, &bottom, &h, &v); /* A rectangle and a point */ /* Possible Python call: f(((0, 0), (400, 300)), (10, 10)) */ } { Py_complex c; ok = PyArg_ParseTuple(args, "D:myfunction", &c); /* a complex, also providing a function name for errors */ /* Possible Python call: myfunction(1+2j) */ }
PyArg_ParseTupleAndKeywords()函数声明以下:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, const char *format, char *kwlist[], ...);
arg和format参数含义同PyArg_ParseTuple()函数,kwdict参数用来接收来自Python运行时的第三参数的关键字词典,kwlist参数是以NULL结尾用于标识参数的字符串列表。成功时PyArg_ParseTupleAndKeywords()返回true,不然返回false,并引起异常。
注意:使用关键字参数时不能分析嵌套元组!传入的关键字参数在kwlist不存在会致使TypeError异常。
下面是一个使用关键字的例子:
#include "Python.h" static PyObject * keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds) { int voltage; char *state = "a stiff"; char *action = "voom"; char *type = "Norwegian Blue"; static char *kwlist[] = {"voltage", "state", "action", "type", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist, &voltage, &state, &action, &type)) return NULL; printf("-- This parrot wouldn't %s if you put %i Volts through it.\n", action, voltage); printf("-- Lovely plumage, the %s -- It's %s!\n", type, state); Py_RETURN_NONE; } static PyMethodDef keywdarg_methods[] = { /* The cast of the function is necessary since PyCFunction values * only take two PyObject* parameters, and keywdarg_parrot() takes * three. */ {"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS, "Print a lovely skit to standard output."}, {NULL, NULL, 0, NULL} /* sentinel */ }; static struct PyModuleDef keywdargmodule = { PyModuleDef_HEAD_INIT, "keywdarg", NULL, -1, keywdarg_methods }; PyMODINIT_FUNC PyInit_keywdarg(void) { return PyModule_Create(&keywdargmodule); }
函数PyArg_ParseTuple()声明以下:
PyObject *Py_BuildValue(const char *format, ...);
它识别一组相似于被PyArg_ParseTuple()识别的format单元,但参数(输入到函数而不是输出)不能是指针,只能是值。它返回一个新的Python对象,适合从Python调用的C函数中返回。
与PyArg_ParseTuple()不一样的是:后者要求其第一个参数是一个元组(由于Python参数列表老是在内部表示为元组),Py_BuildValue()并不老是构建一个元组。它仅在格式字符串包含两个或更多格式单元时才构建元组。若是格式字符串为空,则返回None; 若是它只包含一个格式单元,则返回该格式单元描述的任何对象。要强制它返回一个大小为0或1的元组,使用括号包裹format字符串。
示例(在左边的调用,右边的结果Python值):
Py_BuildValue("") None Py_BuildValue("i", 123) 123 Py_BuildValue("iii", 123, 456, 789) (123, 456, 789) Py_BuildValue("s", "hello") 'hello' Py_BuildValue("y", "hello") b'hello' Py_BuildValue("ss", "hello", "world") ('hello', 'world') Py_BuildValue("s#", "hello", 4) 'hell' Py_BuildValue("y#", "hello", 4) b'hell' Py_BuildValue("()") () Py_BuildValue("(i)", 123) (123,) Py_BuildValue("(ii)", 123, 456) (123, 456) Py_BuildValue("(i,i)", 123, 456) (123, 456) Py_BuildValue("[i,i]", 123, 456) [123, 456] Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456) {'abc': 123, 'def': 456} Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
在C或C++等语言中,程序员负责动态分配和释放堆上的内存,在C中使用函数malloc()和free(),在C++中使用new和delete。咱们将限制如下讨论到C的状况下。
每一个分配的内存块malloc()最终都应该经过一次free()调用返还到可用内存池。在正确的时间调用free()很重要。若是一个块的地址被遗忘,可是free()没有被调用,它占用的内存将不能被重用,直到程序终止,这被称为内存泄漏。另外一方面,若是一个程序继续使用一个已经被调用过free()的内存块,另外一个malloc()调用可能产生与该块重用的冲突,这被称为使用释放的内存。它与引用未初始化的数据有相同的不良后果 - 错误的结果,神秘的崩溃。
内存泄漏的常见缘由是代码执行了一段的不寻常的路径。例如,一个函数能够分配一块内存,作一些计算,而后释放该块。如今若是为函数的计算添加一段错误检测逻辑,而且可能会致使函数提早返回。在过早退出时忘记释放分配的内存块是很容易的,特别是当退出逻辑是后面添加到代码中的时候。这种泄漏一旦被引入,每每不会被长时间检测出来:错误退出只占全部调用的一小部分,而大多数现代机器都有大量的虚拟内存,因此在长时间运行的过程当中频繁调用问题函数才会致使明显的内存泄露。所以经过编码惯例或策略来最大限度地减小这类错误,防止泄漏发生是很是重要的。
因为Python大量使用malloc()和free(),它须要一种策略来避免内存泄漏以及释放内存的使用。所选的方法称为参考计数。原理很简单:每一个对象都包含一个计数器,当某个对象的引用存储在某个地方时,该计数器会递增,而当对该引用的引用被删除时该计数器递减。当计数器达到零时,对象的最后一个引用已被删除,对象被释放。
另外一种策略称为自动垃圾收集。(有时引用计数也被称为垃圾收集策略,所以我使用"自动"来区分二者)自动垃圾收集的一大优点是用户无需显式调用free() 。(另外一个声称的优势是速度或内存使用方面的改进 - 但这并不难)。缺点是对于C,没有真正的便携式自动垃圾收集器,而引用计数能够实现可移植性(只要函数malloc() 而且free()可用 - C标准保证)。也许有一天,一个足够便携的自动垃圾收集器将可用于C,在那以前咱们必须忍受引用计数。
当Python使用传统的引用计数实现时,它还提供了一个循环检测器,用于检测循环引用。这容许应用程序不用担忧建立直接或间接循环引用,而这是仅使用引用计数实现的垃圾收集的弱点。循环引用由包含(可能间接)引用自身的对象组成,所以循环中的每一个对象都有一个非零的引用计数。典型的引用计数实现不能回收任何处于循环引用中的对象内存或者引用,即便循环对象自己再也不有进一步的引用。
循环检测器可以检测垃圾循环并能够回收它们。该gc模块公开了一种运行探测器(collect()功能)的方法,以及配置接口和在运行时禁用探测器的功能。循环检测器被视为可选组件,虽然它是默认包含的,但它能够在构建时使用Unix平台(包括Mac OS X)上--without-cycle-gc的configure脚本选项来禁用,若是循环检测器以这种方式被禁用,gc模块将不可用。
宏Py_INCREF(x)和Py_DECREF(x)用来处理引用计数的递增和递减。Py_DECREF()当计数达到零时也释放对象。为了灵活性,它不直接调用free() - 而是经过调用类型对象中的函数指针,为此(和其余)每一个对象还包含一个指向其类型对象的指针。
如今最大的问题仍然是:什么时候使用Py_INCREF(x)和Py_DECREF(x)?咱们先来介绍一些术语。没有人"拥有"一个物体,可是您能够拥有对象的引用。如今对象的引用计数定义为拥有它的引用的数量。当再也不须要引用时,引用的全部者负责调用Py_DECREF()。引用的全部权能够转移。有三种方式能够处理拥有的引用:传递它、存储它或调用Py_DECREF(),忘记处理拥有的引用会形成内存泄漏。
也能够借用对象的引用,引用的借用者不该该调用Py_DECREF(),引用的借用者不能比被借用者持有对象时间更长。被借用者释放对象后,借用者继续使用对象会有风险,应该彻底避免。
借用引用的优点在于,你不须要关心引用的处理,不会由于提早返回致使内存泄露,缺点就是可能存在使用已经释放了的对象。
借用引用能够经过调用Py_INCREF()变成引用拥有者,这不会影响借用出引用的拥有者的地位 - 它会建立一个新的拥有引用,并给予所有的拥有责任(新拥有者必须正确处理引用就像以前的拥有者那样)。
不管什么时候将对象引用传入或传出函数,它都是函数接口规范的一部分,而无论全部权是否与引用一块儿传输。
大多数返回对象引用的函数都会将引用的全部权传递给它,特别是建立新对象的函数,例如PyLong_FromLong()和Py_BuildValue(),它们将返回对象的全部权交给接收方,即便该对象并非真正的新对象,您仍然会得到对该对象新引用的全部权,例如PyLong_FromLong()维护值的缓存并返回缓存的引用。
例如许多从其余对象中提取对象的功能也会转移引用的全部权,例如PyObject_GetAttrString()。然而有些例外:PyTuple_GetItem(),PyList_GetItem(),PyDict_GetItem()和 PyDict_GetItemString()返回的是元组、列表或字典的借用引用。
函数PyImport_AddModule()一样返回一个借用引用,尽管实际上它可能真的建立了它返回的对象:这是可能的,由于对该对象的拥有引用存储在sys.modules中。
当你将一个对象引用传递给另外一个函数时,一般这个函数会借用你的引用 - 若是它须要存储它,调用Py_INCREF()来成为一个独立的全部者。这个规则有两个重要的例外:PyTuple_SetItem()和 PyList_SetItem(),这些函数取代了传递给它们的物品的全部权 - 即便它们失败了!(请注意PyDict_SetItem()不会接管全部权 - 他们是"正常"的)
当从Python调用C函数时,它会借用调用者的参数的引用。调用者拥有对该对象的引用,因此借用的引用的生命周期将获得保证,直到该函数返回。只有当借用引用必须被存储或传递时,必须经过调用Py_INCREF()转化为拥有引用。
从Python调用的C函数返回的对象引用必须是拥有引用 - 拥有权将从函数传递给调用者。
有几种状况看似无害地使用借用引用会致使问题,这些都与解释器的隐式调用有关,这可能致使引用的全部者释放它。
要了解的第一个也是最重要的案例是在不相关对象上使用Py_DECREF(),同时借用对列表项的引用。例如:
void bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); PyList_SetItem(list, 1, PyLong_FromLong(0L)); PyObject_Print(item, stdout, 0); /* BUG! */ }
该函数首先借用一个引用list[0],而后用0替换list[1]的值,最后打印借用的引用。看起来没什么问题,对吧?但实际上不是!
让咱们按照控制流程进入PyList_SetItem()。该列表拥有对其全部项目的引用,所以当项目1被替换时,它必须处理原始项目1。如今让咱们假设原始项目1是用户定义的类的一个实例,而且让咱们进一步假设类定义了一种 del()方法。若是此类实例的引用计数为1,则处置它将调用其__del__()方法。
因为它是用Python编写的,因此该__del__()方法能够执行任意的Python代码。它也执行了一些致使item引用无效的bug()函数?假设传入bug()的列表能够被__del__()方法访问,它能够执行一个del list[0]
语句,而且假定这是该对象的最后一个引用,它将释放与它关联的内存,从而致使item失效。
一旦知道问题的根源,就很容易想出解决方案:暂时增长引用计数。该函数的正确版本为:
void no_bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); Py_INCREF(item); PyList_SetItem(list, 1, PyLong_FromLong(0L)); PyObject_Print(item, stdout, 0); Py_DECREF(item); }
这是一个真实的故事。一个老版本的Python包含了这个bug的变种,有人花费了大量的时间在C调试器中弄清楚他的__del__()方法为何会失败......
借用引用的第二种状况是涉及线程的变体。一般Python解释器中的多个线程没法相互获取,由于有一个全局锁定保护Python的整个对象空间。可是可使用宏临时释放此锁 Py_BEGIN_ALLOW_THREADS,并使用Py_END_ALLOW_THREADS从新获取它。这在阻塞I/O调用方面很常见,等待I/O完成时让其余线程使用处理器,显然下面的函数与上一个函数具备相同的问题:
void bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); Py_BEGIN_ALLOW_THREADS ...some blocking I/O call... Py_END_ALLOW_THREADS PyObject_Print(item, stdout, 0); /* BUG! */ }
通常来讲,将对象引用做为参数的函数并不指望你传递NULL指针给它们,而且若是你这样作的话,将会致使崩溃。返回对象引用的函数一般仅当发生异常才返回NULL,不测试NULL参数的缘由是函数一般会将它们接收到的对象传递给其余函数 - 若是每一个函数都要测试NULL,则会有大量冗余测试,而且代码运行速度会更慢。
当收到一个可能为NULL的指针时,最好仅在最开始处测试NULL,例如在malloc()或可能引起异常的函数返回处测试。
宏Py_INCREF()和Py_DECREF()不检查NULL指针-可是,它们的变体Py_XINCREF()和Py_XDECREF()会检查。
用于检查特定对象类型的宏(Pytype_Check())不会检查NULL指针 - 由于有不少代码在针对各类不一样的预期类型时,会连续调用几个宏来测试一个对象,若是检查NULL指针的话会产生冗余测试,因此对于检查特定对象类型的宏没有带NULL检查的变体。
C函数调用机制保证传递给C函数的参数列表(args在这个例子中)永远不是NULL - 事实上它保证它老是一个元组。
让一个NULL指针"逃到"Python用户这是一个严重的错误。
能够用C++编写扩展模块,但有些限制。若是主程序(Python解释器)由C编译器编译和连接,则不能使用带构造函数的全局对象或静态对象。若是主程序由C++编译器连接就没有问题。Python解释器调用的函数(特别是模块初始化函数)必须使用extern "C"
声明。没有必要将Python头文件包含在extern "C" {...}
- 若是定义了符号__cplusplus
,它们就会使用这种形式(全部最新的C++编译器都定义了此符号)。
许多扩展模块只是提供了Python中使用的新功能和类型,但有时扩展模块中的代码可能对其余扩展模块有用。例如扩展模块能够实现相似"收集"的类型,其工做方式相似于没有顺序的列表。就像标准的Python列表类型有一个容许扩展模块建立和操做列表的C API同样,这个新的集合类型应该有一组C函数用于直接从其余扩展模块进行操做。
乍一看,这看起来很简单:只需编写函数(没有static声明),提供适当的头文件,并添加C API文档。事实上若是全部的扩展模块老是与Python解释器静态连接的话是没问题的,可是当模块用做共享库时,一个模块中定义的符号可能对另外一个模块不可见,可见性的细节取决于操做系统,一些系统为Python解释器和全部扩展模块(例如Windows)使用一个全局名称空间,而其余系统则须要在模块连接时间(例如AIX)显式导入导入的符号列表或者提供不一样策略的选择(大多数Unix系统),即便符号是全局可见的,其但愿调用的函数的模块可能还没有加载!
所以可移植性不须要对符号可见性作出任何假设,这意味着除了模块的初始化函数以外,扩展模块中的全部符号都应声明为static,以免与其余扩展模块发生名称冲突(如The Module’s Method Table and Initialization Function所述),这意味着从其余扩展模块访问的符号必须以不一样的方式导出。
Python提供了一种特殊的机制来将C级信息(指针)从一个扩展模块传递到另外一个扩展模块:Capsules。Capsule是一个存储void *
指针的Python数据类型。Capsule只能经过C API建立和访问,但它们能够像任何其余Python对象同样传递。特别的是能够分配扩展模块名称空间中的名称给它们,而后其余扩展模块能够导入该模块,检索该名称的值,而后从Capsule检索指针。
Capsules有许多方式导出扩展模块的C API,每一个函数均可以得到本身的Capsule或者全部C API指针均可以存储在一个地址在Capsule中发布的数组中。存储和检索指针的各类任务能够在提供代码的模块和客户模块之间以不一样的方式分配。
不管您选择哪一种方法,正确命名Capsules很是重要。函数PyCapsule_New()接受一个const char *
类型的名称参数,您能够传入一个NULL,但咱们强烈建议您指定一个名称。
特别是用于公开C API的Capsules应按照如下约定命名:
modulename.attributename
PyCapsule_Import()函数能够很是方便的加载Capsule提供的C API,但前提是Capsule的名称符合此惯例,这种行为使得C API用户能够高度确定他们加载的Capsule包含正确的C API。
如下示例演示了一种将导出模块的写入程序的大部分负担放在经常使用库模块的适当位置的方法。它将全部C API指针(示例中只有一个!)存储在一个void指针数组中,该数组成为Capsule的值。与模块相对应的头文件提供了一个宏,它负责导入模块并检索其C API指针,客户端模块只需在访问C API以前调用此宏。
导出模块是对A Simple Example模块spam部分的修改。函数spam.system()
并不直接调用C库函数system()
,而是PySpam_System()函数,现实中它固然会作一些更复杂的事情(好比为每一个命令添加spam"), PySpam_System()函数也被导出到其余扩展模块。
函数PySpam_System()是一个简单的C函数,像其余同样声明为static:
static int PySpam_System(const char *command) { return system(command); }
该函数spam_system()以一种简单的方式进行修改:
static PyObject * spam_system(PyObject *self, PyObject *args) { const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = PySpam_System(command); return PyLong_FromLong(sts); }
在模块的开头,紧跟在行后面
#include "Python.h"
必须添加两行:
#define SPAM_MODULE #include "spammodule.h"
#define
指明头文件被包含在导出模块,而不是客户端模块。最后模块的初始化函数必须注意初始化C API指针数组:
PyMODINIT_FUNC PyInit_spam(void) { PyObject *m; static void *PySpam_API[PySpam_API_pointers]; PyObject *c_api_object; m = PyModule_Create(&spammodule); if (m == NULL) return NULL; /* Initialize the C API pointer array */ PySpam_API[PySpam_System_NUM] = (void *)PySpam_System; /* Create a Capsule containing the API pointer array's address */ c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL); if (c_api_object != NULL) PyModule_AddObject(m, "_C_API", c_api_object); return m; }
请注意PySpam_API声明的是static,不然指针数组在PyInit_spam()终止时会消失!
大部分工做都在头文件中spammodule.h,以下所示:
#ifndef Py_SPAMMODULE_H #define Py_SPAMMODULE_H #ifdef __cplusplus extern "C" { #endif /* Header file for spammodule */ /* C API functions */ #define PySpam_System_NUM 0 #define PySpam_System_RETURN int #define PySpam_System_PROTO (const char *command) /* Total number of C API pointers */ #define PySpam_API_pointers 1 #ifdef SPAM_MODULE /* This section is used when compiling spammodule.c */ static PySpam_System_RETURN PySpam_System PySpam_System_PROTO; #else /* This section is used in modules that use spammodule's API */ static void **PySpam_API; #define PySpam_System \ (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM]) /* Return -1 on error, 0 on success. * PyCapsule_Import will set an exception if there's an error. */ static int import_spam(void) { PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0); return (PySpam_API != NULL) ? 0 : -1; } #endif #ifdef __cplusplus } #endif #endif /* !defined(Py_SPAMMODULE_H) */
为了访问函数PySpam_System(),客户端模块必须在其初始化函数中调用函数(或者说宏)import_spam():
PyMODINIT_FUNC PyInit_client(void) { PyObject *m; m = PyModule_Create(&clientmodule); if (m == NULL) return NULL; if (import_spam() < 0) return NULL; /* additional initialization can happen here */ return m; }
这种方法的主要缺点是文件spammodule.h至关复杂,可是对于每一个导出的函数,其基本结构都是相同的,所以仅须要学习一次。