我是靠谱客的博主 失眠书本,最近开发中收集的这篇文章主要介绍python基础-C扩展原因写扩展库的代码包装代码编译,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

写python的c扩展简介
使用C/C++编写Python模块扩展
Python - 用C扩展编程
使用 C 或 C++ 扩展 Python

原因

  • 添加额外的非python功能。
  • 性能瓶颈的效率提升
  • 专有源代码保密

写扩展库的代码

Extest.c文件包含包含要扩展的C模块,包含fac()和reverse()函数。
并调试完bug。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int fac(int n)
{
if (n<2) return 1;
return n * fac(n-1);
}
char * reverse(char *s)
{
register char t, *p = s, *q = s+(strlen(s)-1);
while(p<q){
t = *p;
*p++ = *q;
*q-- = t;
}
return s;
}
int test()
{
char s[255];
printf("4! == %dn", fac(4));
printf("8! == %dn", fac(8));
printf("12! == %dn", fac(12));
strcpy(s, "abcdef");
printf("reversing 'abcdef', we get '%s'n",reverse(s));
strcpy(s, "madam");
printf("reversing 'madam', we get '%s'n",reverse(s));
return 0;
}

包装代码

  • 包含python头文件
  • 为每个模块的每个函数增加一个Pyobject * Module_func()的包装函数
  • 为每个模块增加一个PyMethodDef ModuleMethods[]的数组
  • 增加木块初始化函数void initModule()

为想被Python环境访问的函数增加一个静态变量,函数的返回值类型为PyObject*,函数名前要加上模块名和一个下划线。比如Extest模块的fac函数,创建包装函数Extest_fac()。这样可以在python中import Extest,然后调用Extest.fac()。

包装函数就是把Python的值传递给C,然后调用C函数处理。当处理完成返回给python的时候,把函数的计算结果转换成python对象,返回给python。

从python到C的转换用PyArg_Parse*系列函数。从C转到python的时候,就用Py_BuildValue()函数。

PyArg_Parse系列函数用法跟C的sscanf 函数很像,接受一个字符串流,根据一个指定格式字符串进行解析,把结果放入到相应的指针所指的变量中。返回值为1表示解析成功,返回值为0表示失败。
Py_BuildValue的用法跟sprintf函数很像,把所有的参数按格式字符串所指定的格式转换成一个python对象。

函数描述
int PyArg_ParseTuple()把python传过来的参数转为C
int PyArg_ParseTupleAndKeywords()把python传过来的参数转为C,但是同时解析关键字参数
PyObject *Py_BuildValue()把C的数据转为python的一个对象或一组对象,然后返回

python和C数据转换的格式符号

format codepython typec type
sstrchar *
zstr/Nonechar * /NULL
iintint
llonglong
cstrchar
dfloatdouble
DcomplexPy_Complex *
O(any)PyObject *
SstrPyStringObject
// 包装函数
static PyObject * Extest_fac(PyObject * self, PyObject * args)
{
int res;
int num;
PyObject * retval;
res = PyArg_ParseTuple(args, "i", &num);
if (!res){
return NULL;
}
res = fac(num);
retval = Py_BuildValue("i", res);
return retval;
}
static PyObject * Extest_doppel(PyObject *self, PyObject * args)
{
char * orig_str;
char * dupe_str;
PyObject * retval;
if(!PyArg_ParseTuple(args, "s", $orig_str)) return NULL;
retval = Py_BuildValue("ss", orig_str, dupe_str=reverse(strdup(orig_str)));
free(dupe_str);
return retval;
}
static PyObject * Extest_test(PyObject * self, PyObject *args)
{
test();
return Py_BuildValue("");
}

‘ss’格式让Py_BuildValue()函数生成了一个包含两个字符串的tuple。

C代码中注意内存泄露。复制字符串后在函数退出前要释放。
为每个模块增加PyMethodDef ModuleMethods[]数组
完成包装函数后,需要把函数列在某个地方,编译python解释器能够导入并调用。
这是个二维数组,里面每个数组包含一个函数,最后放一个NULL数组表示列表的结束。

// 模块的方法数组PyMethodDef ModuleMethods[]
//METH_VARARGS常量表示参数以tuple形式传入.如果使用PyArg_ParseTupleAndKeywords()函数解析命名
//参数需要使用METH_VARARGS与METH_KEYWORDS常量进行逻辑与运算。
static PyMethodDef ExtestMethods[]{
{"fac", Extest_fac, METH_VARARGS},
{"doppel", Extest_doppel, METH_VARARGS},
{'test', Extest_test, METH_VARARGS}
{NULL, NULL},
}

增加模块初始换函数void initModule()
调用Py_InitModule()函数,并把模块名和ModuleMethods[]数组的名字穿进去。

//增加模块初始化函数void initModule()
void initExtest()
{
Py_InitModule("Extest", ExtestMethods);
}

编译

  • 创建setup.py
  • 通过运行setup.py来编译和连接代码
  • 从python中导入扩展模块

创建setup.py,编译主要由setup()函数完成。这个函数调用之前的所有代码。每个扩展模块需要一个Extension实例。
Extension(MOD,sources=['Extest.c'])
名字为模块名,sources参数是所有源代码的文件列表。
setup()函数第一个参数表示要编译哪些东西,一个列表列出要编译的对象。

from distutils.core import setup, Extension
MOD = 'Extest'
setup(name=MOD, ext_modules=[Extension(MOD,sources=['Extest.c'])])

编译和连接代码
运行setup.py build命令可以开始编译我们的扩展了。

$python setup.py build

导入和测试
扩展会被创建在运行setup.py脚本所在的目录下的build/lib.*目录下。可以切换到哪个目录中测试新木块,也可以使用命令安装到python中

$python setup.py install

然后就可以在python中导入和使用模块中的函数了。

最后

以上就是失眠书本为你收集整理的python基础-C扩展原因写扩展库的代码包装代码编译的全部内容,希望文章能够帮你解决python基础-C扩展原因写扩展库的代码包装代码编译所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部