概述
- python扩展模块开发
- 准备说明
- 注意事项
- 实用API说明
- 示例演示
- 总结
python扩展模块开发
- 在某些情况下,为了追求高性能或绕过python的一些限制(比如GIL全局锁)等,我们可以使用c或c++来为python开发特殊的模块,提升python效率(如cjson,cpickle等库)。
- python 提供了一套非常完整的c api,也有非常丰富的文档,官方文档见:https://docs.python.org/2/c-api/
- 这里主要介绍一些必要实用的c api函数,开发实例和c api以python2.7为准(因为公司用的就是python2.7)
- 如果要看更详细的实例,之后我会在我的一些c/c++文章中加入python的接口实现
准备说明
- Python.h文件: 此文件是python c api的入口文件,所有api都包含在此文件里面
- Py_和_Py: python c api的方法与变量前缀,在代码中尽量不要使用此前缀,避免混乱
- PyObject: python对象,包含引用计数与对象指针,所以的输入输出都通过此对象
- bool: 布尔类型,C是没有bool类型的,python c api的bool类型定义在asdl.h中,形式如下:typedef enum {false, true} bool;,即false=0,true=1
注意事项
- windows环境编译:需要将python27.lib改为python27_d.lib,并且要注意编译的位数,如果编译的是32位,那链接的必须是python32位的库。编译后的lib库,如果要使用,需要将.lib后缀修改为.pyd。另外如果要在其它机器上使用此pyd库,需要注意依赖的lib是否都存在,否则会报“找不到指定的模块”
- 接口描述列表:列表最后一个元素必须是{ 0, 0 , 0, 0 }结尾
- 模块初始化:初始化函数定义格式如下:PyMODINIT_FUNC init{模块名},例如PyMODINIT_FUNC inittest(void)
实用API说明
Py_INCREF函数
函数原型:void Py_INCREF(PyObject *o)
功能说明:增加对象引用计数,会影响对象的GC回收。注意:对象不能为NULL,如果不知道对象是否为空,请使用Py_XINCREF()
返回说明:无返回值
O:要操作的对象
注意:在开发扩展模块时,基本不需要使用到此函数Py_DECREF函数
函数原型:void Py_DECREF(PyObject *o)
功能说明:减少对象引用计数,会影响对象的GC回收。注意:对象不能为NULL,如果不知道对象是否为空,请使用Py_XDECREF()
返回说明:无返回值
O:要操作的对象
注意:在开发扩展模块时,一般情况下,只有对python c对象操作错误的时候,才需要减少对象的引用计数Py_InitModule函数
函数原型:PyObject* Py_InitModule(char *name, PyMethodDef *methods)
功能说明:根据名称和函数表创建一个新的模块对象
返回说明:返回一个新的模块对象
name:模块名称
methods:模块函数描述表
注意:这个函数是扩展模块必须的Py_InitModule3函数
函数原型:PyObject* Py_InitModule3(char *name, PyMethodDef *methods, char *doc)
功能说明:根据名称和函数表创建一个新的模块对象,与Py_InitModule函数功能相似,多一个doc,用于模块描述
返回说明:返回一个新的模块对象
doc:模块描述Py_InitModule函数示例
#include <Python.h>
#ifdef WIN32
// 注意:如果是在windows上开发,需要将python27.lib改为python27_d.lib
// 注意:如果是在windows上开发,32位与64位程序编译需要链接同样位数的python库
#pragma comment(lib,"python27_d.lib")
#endif
//测试函数
static PyObject *test(PyObject *self, PyObject *args){
return Py_BuildValue("i", 0);
}
//接口说明列表
static PyMethodDef apiMethods[] = {
{ "test", test, METH_VARARGS, "测试函数" },
// 注意:接口列表最后需要以{ 0, 0 , 0, 0 }结尾
{ 0, 0 , 0, 0 }
};
// 初始化模块,注意:init后面需要跟模块名称一样
PyMODINIT_FUNC inittest(void){
Py_InitModule("test", apiMethods);
}
PyArg_ParseTuple函数
函数原型:int PyArg_ParseTuple(PyObject *args, const char *format, …)
功能说明:解析一个函数的参数,该函数只将位置参数转换为局部变量
返回说明:成功返回true,失败返回false,并抛出异常
args:参数列表
format:参数格式化说明字符,具体格式符见官网:https://docs.python.org/2/c-api/arg.html?highlight=pyarg_parsetuple#c.PyArg_ParseTuple
局部变量:按format格式转换后的参数将保存在局部变量中,局部变量都是一些数据指针,可以指定多个,但要与format说明的顺序与类型一致Py_BuildValue函数
函数原型:PyObject* Py_BuildValue(const char *format, …)
功能说明:按format格式和一系列值来创建一个新的值,这个新值是一个python类型对象,可在python中使用
返回说明:成功返回新值,失败返回错误代码或NULL,如果返回NULL,则会抛出异常
format:格式化说明字符,具体格式符见官网:https://docs.python.org/2/c-api/arg.html?highlight=pyarg_parsetuple#c.Py_BuildValue
值:要被format格式的值PyList_New函数
函数原型:PyObject* PyList_New(Py_ssize_t len)
功能说明:创建一个长度为len的新列表
返回说明:成功返回新列表,失败返回NULL
len:指定列表长度
更多列表操作函数:见官网https://docs.python.org/2/c-api/list.htmlPyList_SetItem函数
函数原型:int PyList_SetItem(PyObject *list, Py_ssize_t index, PyObject *item)
功能说明:向列表插入数据,列表操作最频繁的函数
返回说明:成功返回0,失败返回-1
list:PyList_New创建的列表对象
index:插入元素的位置
item:要插入的元素PyDict_New函数
函数原型:PyObject* PyDict_New()
功能说明:创建一个新的空字典
返回说明:成功返回新空字典,失败返回NULL
更多字典操作函数:见官网https://docs.python.org/2/c-api/dict.htmlPyDict_SetItemString函数
函数原型:int PyDict_SetItemString(PyObject *p, const char *key, PyObject *val)
功能说明:向字典p插入一个Key和对应的值,key是一个字符串。注意:如果Key也是一个对象,请使用PyDict_SetItem函数,但注意key必须是hashable(https://docs.python.org/2/glossary.html#term-hashable)
返回说明:成功返回0,失败返回-1
p:PyDict_New创建的一个字典对象
key:数据的Key值,一个字符串
val:数据的具体值
示例演示
- 通过python c api接口开发判断一个数是否为质数的扩展模块
#include <Python.h>
#ifdef WIN32
#pragma comment(lib,"python27_d.lib")
#endif
bool isPrimeNumber(unsigned long x) {
if (x <= 3) {
//如果x小于2,表示此时x即不是质数也不是合数,而2和3都是质数
return (x > 1);
}
//如果x能被[2 (x-1)]的整数整除,那么表示x是合数,并不是质数
for (unsigned long i = 2; i < x; i++) {
if (x % i == 0) {
//能被整除表示不是质数
return false;
}
}
//如果无法被[2 (x-1)]的整数整除,那么x就是质数
return true;
}
static PyObject *isPrimeNumber(PyObject *self, PyObject *args) {
int ret = 0, number = 0;
// 获取参数
ret = PyArg_ParseTuple(args, "i", &number);
if (ret <= 0) {
return Py_BuildValue("i", -1);
}
// 如果是质数返回0,不是或错误返回-1
if (isPrimeNumber((unsigned long)number)) {
return Py_BuildValue("i", 0);
}
return Py_BuildValue("i", -1);
}
// 接口说明列表
static PyMethodDef apiMethods[] = {
{ "isPrimeNumber", isPrimeNumber, METH_VARARGS, "判断一个数是否是质数" },
{ 0, 0 , 0, 0 }
};
// 关联接口列表
PyMODINIT_FUNC initisPrimeNumber(void) {
Py_InitModule("isPrimeNumber", apiMethods);
}
// 通过vs2015编译(32位应用程序),使用python2.7 32位版本运行成功,结果如下
// 记得处理winodws编译注意事项
/*
Python 2.7.9 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import isPrimeNumber
>>> print isPrimeNumber.isPrimeNumber(3)
0
>>> print isPrimeNumber.isPrimeNumber(4)
-1
>>>
*/
// 通过g++编译,使用python2.6库,但2.7也可以使用
// 编译命令如下:g++ -o isPrimeNumber.so -I /usr/include/python2.6 isPrimeNumber.cpp -fPIC -shared
// -I用于指定Python.h的路径
/*
Python 2.7.9
[GCC 4.4.7 20120313] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import isPrimeNumber
>>> print isPrimeNumber.isPrimeNumber(3)
0
>>> print isPrimeNumber.isPrimeNumber(4)
-1
>>>
*/
- 一个简单的使用python c api列表字典的示例
#include <Python.h>
#ifdef WIN32
#pragma comment(lib,"python27_d.lib")
#endif
static PyObject *testListOrDict(PyObject *self, PyObject *args) {
int ret = 0, number = 0;
// 获取参数
ret = PyArg_ParseTuple(args, "i", &number);
if (ret <= 0) {
return Py_BuildValue("");
}
// 创建列表,字典
PyObject *pyList = NULL; //python列表
PyObject *pyDict = NULL; //python字典
pyList = PyList_New(1);
if (NULL == pyList) {
// 如果创建失败就返回None
return Py_BuildValue("");
}
pyDict = PyDict_New();
if (NULL == pyDict) {
// 如果创建失败就返回None
return Py_BuildValue("");
}
// 加入数据到字典
PyDict_SetItemString(pyDict, "number", Py_BuildValue("i", number));
// 加入数据到列表
PyList_SetItem(pyList, 0, pyDict);
return pyList;
}
// 接口说明列表
static PyMethodDef apiMethods[] = {
{ "testListOrDict", testListOrDict, METH_VARARGS, "测试使用列表与字典" },
{ 0, 0 , 0, 0 }
};
// 关联接口列表
PyMODINIT_FUNC inittestListOrDict(void) {
Py_InitModule("testListOrDict", apiMethods);
}
// vs2015编译运行
/*
Python 2.7.9 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import testListOrDict
>>> print testListOrDict.testListOrDict(3)
[{'number': 3}]
>>>
*/
// g++编译运行,使用python2.6库,但2.7也可以使用
// 编译命令:g++ -o testListOrDict.so -I /usr/include/python2.6 testListOrDict.cpp -fPIC -shared
/*
Python 2.7.9
[GCC 4.4.7 20120313] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import testListOrDict
>>> print testListOrDict.testListOrDict(3)
[{'number': 3}]
>>>
*/
总结
- 使用python c api来扩展python模块可以说是非常高效的,比使用ctypes等库来调用so高效方便很多。特别是在处理某些高并发任务的时候,使用c的线程库来处理任务,可以更高效的利用多核cpu。又比如使用c来实现syn端口扫描,然后使用python c api来直接扩展python使用,可以很方便高效的处理一些python不好处理的任务
最后
以上就是标致世界为你收集整理的python扩展模块开发python扩展模块开发的全部内容,希望文章能够帮你解决python扩展模块开发python扩展模块开发所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复