• 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")
#endifbool 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,不是或错误返回-1if (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) {// 如果创建失败就返回Nonereturn Py_BuildValue("");}pyDict = PyDict_New();if (NULL == pyDict) {// 如果创建失败就返回Nonereturn 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扩展模块开发相关推荐

  1. Windows下安装Python扩展模块提示“Unable to find vcvarsall.bat”的问题(转载)

    Unable to find vcvarsall.bat的问题描述 问题分析 总结 提示: 如果你只是想知道自己需要安装哪个版本的Visual Studio请直接查看本文最后一个小节的内容. 一.问题 ...

  2. python利用写模块_使用C++编写python扩展模块

    简介 长话短说,这里说的扩展Python功能与直接用其它语言写一个动态链接库,然后让Python来调用有点不一样(虽然本质是一样的).而是指使用Python本身提供的API,使用C++来对Python ...

  3. python深入和扩展_加速方案 — Python扩展模块

    原标题:加速方案 - Python扩展模块 ctypes(一)- 初识 这章我们介绍Python的扩展名之ctypes,教大家认识ctypes.喜欢Python的读者们可以加Python学习交流群:5 ...

  4. 《Python爬虫开发与项目实战》——第1章 回顾Python编程 1.1 安装Python

    本节书摘来自华章计算机<Python爬虫开发与项目实战>一书中的第1章,第1.1节,作者:范传辉著,更多章节内容可以访问云栖社区"华章计算机"公众号查看 第1章 回顾P ...

  5. 可爱的python测试开发库(python测试开发工具库汇总)

    2019独角兽企业重金招聘Python工程师标准>>> 欢迎转载,转载请注明来源: github地址 谢谢点赞 本文地址 Python测试开发库 参考资料 https://githu ...

  6. 山东省一流本科课程“Python应用开发”课程中的思政元素

    推荐图书: <Python程序设计(第3版)>,(ISBN:978-7-302-55083-9),清华大学出版社,2020年6月第1次印刷 京东.天猫已上架,当当很快会上架,可以选择自己常 ...

  7. Python图形用户界面设计-Delphi For Python高级开发教程

    目录 简介........................................................................ 5 谁应该阅读这本教程........... ...

  8. python前端开发招聘_web前端和python学哪个出来工资高?

    展开全部 题主的意图说得很明显了e68a84e8a2ad62616964757a686964616f31333433646436,就是为了更好的就业,获得一份不错的薪资.那么我们首先来看一下Pytho ...

  9. sklearn:Python语言开发的通用机器学习库

    引言:深入理解机器学习并全然看懂sklearn文档,须要较深厚的理论基础.可是.要将sklearn应用于实际的项目中,仅仅须要对机器学习理论有一个主要的掌握,就能够直接调用其API来完毕各种机器学习问 ...

  10. Python 多进程开发与多线程开发

    我们先来了解什么是进程? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本:进程 ...

最新文章

  1. 一台服务器能承载多少用户_一台入门级服务器能为你的办公应用带来哪些效率?评测告诉你...
  2. Hyperledger Fabric 链码(1) 类型
  3. 在Linux CentOS上编译CoreCLR
  4. 跳槽必看:产品经理面试试题汇总
  5. c#如何通过ftp上传文件_ftp自动上传工具,ftp自动上传工具如何自动上传文件
  6. 【程序设计】变量的作用域
  7. android-getTextSize返回值是以像素(px)为单位的,setTextSize()以sp为单位
  8. ERROR: Could not create or update '/usr/local/nagios/var/nagios.configtest'
  9. 如何打开.DAT格式的测风数据?
  10. 【解决】RuntimeError:Trying to backward throughthe graph a second time
  11. unity给头发添加物理_U3D实时渲染教程之角色头发各向异性表达(上)
  12. 永久免费虚拟主机、免费云服务器,白嫖党福利!
  13. 重磅 | Stratifyd实力入选《2022中国数据智能产业图谱1.0》
  14. 企业微信的渠道活码怎么用?到底有什么好处?
  15. 微信群管理助手哪里弄的?
  16. 《暗黑破坏神》经典爆笑小说
  17. java疯狂讲义输入输出视频_疯狂JAVA讲义---第十五章:输入输出(上)流的处理和文件...
  18. 产品化与项目之间的关系
  19. iframe的常用方法
  20. MySQL 8.0 安装教程

热门文章

  1. 201671030123+词频统计软件项目报告
  2. MySQL中round函数
  3. 笔记本计算机内部部件,笔记本内部硬件构造有哪些
  4. Python利用requests库爬取百度文库文章
  5. ElasticSearch文档检索,分词、精确匹配、多条件
  6. WebWork + Spring + iBatis + MySql 实例(Jonson)
  7. 如何用运营思维,搭建会员运营体系
  8. java excel 字体_java中Excel字体的设置,背景和纹理的操作
  9. maya! board_老司机都在用的30款maya常用插件
  10. dota2 自定义官方服务器,起源2引擎DOTA2重生设置 自定义房间创建