未加星标

快速实现python c扩展模块

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二05 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏
快速实现python c扩展模块

40分钟前来源:cnblogs

1 python扩展模块的组成

在python中,对于一些和系统相关的模块或者对性能要求很高的模块,通常会把这个模块C化。扩展模块中主要包含下面几个部分:

init函数,函数名为:init+模块名,这个函数负责初始化模块,包括设置模块中的方法、对象和其它相关数据的初始化。这个函数是必须的,在脚本中第一次导入这个模块的时候,会先执行这个方法。 定义模块方法描述表,它是一个static类型的PyMethodDef数据结构,用来描述模块中定义的方法。 C函数定义,这些函数是方法描述表中方法的具体实现。 如果模块中定义了类,那么还要定义类方法描述表和对象类型。 2 实现python扩展模块实例

当然,有了上面的组成部分,你还是不知道怎么实现一个模块,下面就用官方的一个例子来演示怎么实现一个python扩展模块,这个扩展模块用来实现在python中执行命令行命令。

// spam.c
1 #include "Python.h" 2 3static PyObject *SpamError; 4 5static PyObject * 6 spam_system(PyObject *self, PyObject *args) 7{ 8constchar *command; 9int sts; 1011if (!PyArg_ParseTuple(args, "s", &command)) 12return NULL; 13 sts = system(command); 14if (sts < 0) { 15 PyErr_SetString(SpamError, "System command failed"); 16return NULL; 17 } 18return PyLong_FromLong(sts); 19} 2021static PyMethodDef SpamMethods = { 22 {"system", spam_system, METH_VARARGS, 23"Execute a shell command."}, 24 {NULL, NULL, 0, NULL} /* Sentinel */25}; 2627PyMODINIT_FUNC 28 initspam(void) 29{ 30 PyObject *m; 3132 m = Py_InitModule("spam", SpamMethods); 33if (m == NULL) 34return; 3536 SpamError = PyErr_NewException("spam.error", NULL, NULL); 37 Py_INCREF(SpamError); 38 PyModule_AddObject(m, "error", SpamError); 39 }

上面的initspam是模块的初始化函数,函数开始调用了Py_InitModule初始化了一个名为spam的模块,模块的方法描述表是SpamMethods,它描述了模块有个名为system的方法,这个方法的c/c++实现是spam_system函数。从spam_system函数可以看到它就是调用system函数执行从python传过来的命令。有了上面的代码,我们怎样在python中使用了?很简单,先将上面代码编译成动态链接库,然后直接在python中用import语句导入这个模块就可以用了。在windows下的用vs编译就行,不过在vs建立了dll工程后,需要设置下工程的属性,目的是设置python扩展涉及到的头文件路径和动态库。具体设置如下:先在VC++目录中设置include和lib路径,然后在链接器的附加依赖项中添加python27.lib库。


快速实现python c扩展模块
快速实现python c扩展模块

设置好后直接编译就可以了,将编译生成的dll文件后缀名改成pyd,然后就可以在python中直接用import导入这个模块了。是不是非常的简单!!!!


快速实现python c扩展模块
3 实现python扩展模块中定义类

上面的实现是在模块中定义函数来实现执行命令行命令,我们也可以在模块中定义类,然后用类的方法来执行这个命令。代码如下:

// spam.c
1 #include "Python.h" 2 3static PyObject *SpamError; 4 5static PyObject * 6 spam_system(PyObject *self, PyObject *args) 7{ 8constchar *command; 9int sts; 1011if (!PyArg_ParseTuple(args, "s", &command)) 12return NULL; 13 sts = system(command); 14if (sts < 0) { 15 PyErr_SetString(SpamError, "System command failed"); 16return NULL; 17 } 18return PyLong_FromLong(sts); 19} 2021static PyMethodDef SpamMethods = { 22 {"system", spam_system, METH_VARARGS, 23"Execute a shell command."}, 24 {NULL, NULL, 0, NULL} /* Sentinel */25}; 2627 PyTypeObject *SpamType = NULL; 2829PyMODINIT_FUNC 30 initspam(void) 31{ 32static PyTypeObject _SpamType = { 33 PyObject_HEAD_INIT(NULL) 340, // ob_size35"spam.Spam", // tp_name36sizeof(PyObject), // tp_basicsize370, // tp_itemsize380, // tp_dealloc390, // tp_print400, // tp_getattr410, // tp_setattr420, // tp_compare430, // tp_repr440, // tp_as_number450, // tp_as_sequence460, // tp_as_mapping470, // tp_hash480, // tp_call490, // tp_str500, // tp_getattro510, // tp_setattro520, // tp_as_buffer53 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , // tp_flags540, // tp_doc550, // tp_traverse560, // tp_clear570, // tp_richcompare580, // tp_weaklistoffset590, // tp_iter600, // tp_iternext61 SpamMethods, // tp_methods620, // tp_members630, // tp_getset640, // tp_base650, // tp_dict660, // tp_descr_get670, // tp_descr_set680, // tp_dictoffset690, // tp_init700, // tp_alloc71 PyType_GenericNew, // tp_new72 }; 7374 PyObject *m; 7576 m = Py_InitModule("spam", NULL); 77if (m == NULL) 78return; 79if (PyType_Ready(&_SpamType) < 0) 80return; 81 SpamType = &_SpamType; 82 Py_INCREF(SpamType); 83 PyModule_AddObject(m, "Spam", (PyObject*)SpamType); 84 SpamError = PyErr_NewException("spam.error", NULL, NULL); 85 Py_INCREF(SpamError); 86"error", SpamError); 87 }

上面的代码与之前的代码只是多了个Spam类的定义,使用的时候通过Spam的实例化对象来调用system函数。


快速实现python c扩展模块
4 Python/C API涉及的引用计数问题通过上面的例子,是不是觉得写python的C扩展模块非常的简单呢?其实不然,主要是python中有个引用计数问题,在写扩展模块的时候必须非常小心的处理,否则很有容易导致内存泄露。根据python官方的定义,在Python/C API中,引用计数的行为被归纳为三种:new reference、borrow reference和steal reference,前两种用于描述返回PyObject*类型的函数对返回的这个对象的引用计数的行为;后一种用于将一个PyObject*类型传入函数后,函数对这个对象的引用计数的行为。new referenc表示函数将这个对象引用的所有权转交给函数调用者了,由函数的调用者来管理这个引进的计数,也就是说调用者不用这个引用的时候必须显示的调用 Py_DECREF或者Py_XDECREF来释放这个引用,典型的函数是PyObject_、PyNumber_、PySequence_和PyMapping_;borrow reference与new reference刚好相反,表示函数的调用者只管用这个引用,不用关心它的引用计数,用完了也不用显示调用Py_DECREF或者Py_XDECREF来释放这个引用,典型的函数是PyList_GetItem、PyTuple_GetItem;steal reference表示函数内部只会使用这个引用,不会调用Py_INCREF来增加这个引用的引用计数,相当于“偷了”被调用者的一个引用计数,典型的函数是PyList_SetItem和PyTuple_SetItem。因此,在编写C扩展的时,如果遇到某个Python/C API不确定是哪种reference的时候,建议查下官方文档,文档中会明确的说明这个函数是哪类reference(如下图所示),这样能大大减少引用计数的问题。
快速实现python c扩展模块

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

主题: C++数据结构WindowsPython数据ETHFUAU其实所有权
分页:12
转载请注明
本文标题:快速实现python c扩展模块
本站链接:http://www.codesec.net/view/530668.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(36)