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的接口实现
准备说明
- 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")
#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扩展模块开发相关推荐
- Windows下安装Python扩展模块提示“Unable to find vcvarsall.bat”的问题(转载)
Unable to find vcvarsall.bat的问题描述 问题分析 总结 提示: 如果你只是想知道自己需要安装哪个版本的Visual Studio请直接查看本文最后一个小节的内容. 一.问题 ...
- python利用写模块_使用C++编写python扩展模块
简介 长话短说,这里说的扩展Python功能与直接用其它语言写一个动态链接库,然后让Python来调用有点不一样(虽然本质是一样的).而是指使用Python本身提供的API,使用C++来对Python ...
- python深入和扩展_加速方案 — Python扩展模块
原标题:加速方案 - Python扩展模块 ctypes(一)- 初识 这章我们介绍Python的扩展名之ctypes,教大家认识ctypes.喜欢Python的读者们可以加Python学习交流群:5 ...
- 《Python爬虫开发与项目实战》——第1章 回顾Python编程 1.1 安装Python
本节书摘来自华章计算机<Python爬虫开发与项目实战>一书中的第1章,第1.1节,作者:范传辉著,更多章节内容可以访问云栖社区"华章计算机"公众号查看 第1章 回顾P ...
- 可爱的python测试开发库(python测试开发工具库汇总)
2019独角兽企业重金招聘Python工程师标准>>> 欢迎转载,转载请注明来源: github地址 谢谢点赞 本文地址 Python测试开发库 参考资料 https://githu ...
- 山东省一流本科课程“Python应用开发”课程中的思政元素
推荐图书: <Python程序设计(第3版)>,(ISBN:978-7-302-55083-9),清华大学出版社,2020年6月第1次印刷 京东.天猫已上架,当当很快会上架,可以选择自己常 ...
- Python图形用户界面设计-Delphi For Python高级开发教程
目录 简介........................................................................ 5 谁应该阅读这本教程........... ...
- python前端开发招聘_web前端和python学哪个出来工资高?
展开全部 题主的意图说得很明显了e68a84e8a2ad62616964757a686964616f31333433646436,就是为了更好的就业,获得一份不错的薪资.那么我们首先来看一下Pytho ...
- sklearn:Python语言开发的通用机器学习库
引言:深入理解机器学习并全然看懂sklearn文档,须要较深厚的理论基础.可是.要将sklearn应用于实际的项目中,仅仅须要对机器学习理论有一个主要的掌握,就能够直接调用其API来完毕各种机器学习问 ...
- Python 多进程开发与多线程开发
我们先来了解什么是进程? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本:进程 ...
最新文章
- 一台服务器能承载多少用户_一台入门级服务器能为你的办公应用带来哪些效率?评测告诉你...
- Hyperledger Fabric 链码(1) 类型
- 在Linux CentOS上编译CoreCLR
- 跳槽必看:产品经理面试试题汇总
- c#如何通过ftp上传文件_ftp自动上传工具,ftp自动上传工具如何自动上传文件
- 【程序设计】变量的作用域
- android-getTextSize返回值是以像素(px)为单位的,setTextSize()以sp为单位
- ERROR: Could not create or update '/usr/local/nagios/var/nagios.configtest'
- 如何打开.DAT格式的测风数据?
- 【解决】RuntimeError:Trying to backward throughthe graph a second time
- unity给头发添加物理_U3D实时渲染教程之角色头发各向异性表达(上)
- 永久免费虚拟主机、免费云服务器,白嫖党福利!
- 重磅 | Stratifyd实力入选《2022中国数据智能产业图谱1.0》
- 企业微信的渠道活码怎么用?到底有什么好处?
- 微信群管理助手哪里弄的?
- 《暗黑破坏神》经典爆笑小说
- java疯狂讲义输入输出视频_疯狂JAVA讲义---第十五章:输入输出(上)流的处理和文件...
- 产品化与项目之间的关系
- iframe的常用方法
- MySQL 8.0 安装教程
热门文章
- 201671030123+词频统计软件项目报告
- MySQL中round函数
- 笔记本计算机内部部件,笔记本内部硬件构造有哪些
- Python利用requests库爬取百度文库文章
- ElasticSearch文档检索,分词、精确匹配、多条件
- WebWork + Spring + iBatis + MySql 实例(Jonson)
- 如何用运营思维,搭建会员运营体系
- java excel 字体_java中Excel字体的设置,背景和纹理的操作
- maya! board_老司机都在用的30款maya常用插件
- dota2 自定义官方服务器,起源2引擎DOTA2重生设置 自定义房间创建