我是靠谱客的博主 标致世界,最近开发中收集的这篇文章主要介绍python扩展模块开发python扩展模块开发,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

  • 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的接口实现

准备说明

  1. Python.h文件: 此文件是python c api的入口文件,所有api都包含在此文件里面
  2. Py_和_Py: python c api的方法与变量前缀,在代码中尽量不要使用此前缀,避免混乱
  3. PyObject: python对象,包含引用计数与对象指针,所以的输入输出都通过此对象
  4. bool: 布尔类型,C是没有bool类型的,python c api的bool类型定义在asdl.h中,形式如下:typedef enum {false, true} bool;,即false=0,true=1

注意事项

  1. windows环境编译:需要将python27.lib改为python27_d.lib,并且要注意编译的位数,如果编译的是32位,那链接的必须是python32位的库。编译后的lib库,如果要使用,需要将.lib后缀修改为.pyd。另外如果要在其它机器上使用此pyd库,需要注意依赖的lib是否都存在,否则会报“找不到指定的模块”
  2. 接口描述列表:列表最后一个元素必须是{ 0, 0 , 0, 0 }结尾
  3. 模块初始化:初始化函数定义格式如下: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.html

  • PyList_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.html

  • PyDict_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扩展模块开发所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部