python基础-C扩展

本文介绍了如何使用C/C++编写Python模块扩展,以添加额外功能和优化性能瓶颈。通过创建包装函数,将C代码与Python接口结合,实现Python环境中对C函数的调用。详细步骤包括编写C代码、创建包装函数、定义PyMethodDef数组及初始化模块。文章还提及了编译和导入扩展模块的过程,涉及setup.py脚本的使用。
摘要由CSDN通过智能技术生成

写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! == %d\n", fac(4));
    printf("8! == %d\n", fac(8));
    printf("12! == %d\n", 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中导入和使用模块中的函数了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值