用c写python的拓展模块

用c写python的拓展模块

需要为python程序写C拓展模块的情况

  • 写python不具有的功能
  • 复杂算法提高程序的性能
  • 隐藏核心算法的代码

以下是测试的开发环境

  • Linux version 2.6.32-358.el6.i686
  • Python 2.6.6 (r266:84292, Oct 12 2012, 14:36:13)
  • gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

利用C的API写python的拓展模块

首先编写extest1.c文件,它实现了两个功能强大的函数,计算整数阶乘和字符串反转。如下:

/**************************************************
File name: extest1.c
Author: 
mail: 
Create Time: 2016-12-06 11:38
Copyright 2016 , all rights reserved. 
****************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extest1.h"
int fac (int n)
{
    if (n<2)
    {
        return 1;
    }
    else if (n>=2)
        return n*fac (n-1);
}

char *reverse( char *s )
{
    char *p,*q;
    p=s;
    q=s+(strlen(s)-1);
    char tmp;
    while (p < q)
    {   
        tmp = *p; 
        *p++ = *q;
        *q-- = tmp;
    }
    return s;
}

为了方便别的文件引用这两个函数,给它写个头文件extest1.h,让别的文件方便引用,如下:

int fac (int n); 
char *reverse( char *s );

函数的功能已经完成了,我们再写个主函数的文件mian.c测试一下:

/**************************************************
    > File name: main.c
    > Author: 
    > mail: 
    > Create Time: 2016-12-09 17:08
    > Copyright 2016 , all rights reserved. 
****************************************************/
#include <stdio.h>
#include <string.h>
#include "extest1.h"
int main(int argc, char* argv[])
{
        int n=0;
        char s[20];
        char t[20];
        printf("input a interger:\n");
        scanf("%d",&n);
        getchar();//get the enter
        printf("%d 的阶乘是 %d\n",n,fac(n));

        printf("input a str ,which's length less than 19 : \n");
        scanf("%s",&s);
        strncpy(t,s,19);
        printf("%s 的字符串反转是 %s \n",s,reverse(t));
    return 0;
}

文件写好了之后编译它。

gcc -o a.out extest1.c main.c 

执行a.out结果如下:

[root@localhost candpy]# ./a.out 
input a interger:
4
4 的阶乘是 24
input a str ,which's length less than 19 : 
helloworld!   
helloworld! 的字符串反转是 !dlrowolleh 

好了,C的功能强大的模块已经写好了,现在开始把它变成python的一个拓展。

Python是c语言编写的,它可以直接读C形成的动态库。但是,对于C的数据类型、函数类型,Python并不认识,不能直接用,需要进行一次封装,让他们变成Python识别的类型。其中关于Python数据类型,在C语言中的声明在头文件Python.h。

例如,要对上面的函数reverse 创建一个python的封装函数。
首先,必须创建一个以static PyObject* 的类型的函数。 封装函数的名字最好以Module_func() 形式命名,Module是想创建的python模块名,func是函数名。(这个名称我测试了一下,可以不以这种方式命名,程序是识别的时候不是通过这里的命名规则关联,只是这样规范一点。)
现在,该函数就像其他的python模块中的函数一样,通过Module.func()的形式调用了。
以第一个函数fac为例,名称如下:

static PyObject * Extest_fac ( PyObject *self,PyObject *args )

其中的输入参数第一个*self是默认的,不用管。第二个参数args对应原来函数fac中的输入参数,是python格式的。
在函数中需要转换成c格式的,调用C模块中相应的函数fac,再将输出的C格式转换成Python返回。
完整函数如下:

//#include "/usr/include/python2.6/Python.h"
#include "Python.h"
#include <stdio.h>
#include "extest1.h"

static PyObject * Extest_fac ( PyObject *self,PyObject *args ) {


    int num;

    if (!PyArg_ParseTuple (args,"i",&num)) return NULL;

    return ((PyObject*) Py_BuildValue( "i" , fac(num) )) ;
}
//输出原始字符和一个反转字符的二元组。
static PyObject *Extdoppel ( PyObject *self , PyObject * args ){
    char *orig_str,*copy_str;
    if (!PyArg_ParseTuple(args,"s",&orig_str) ) return NULL;
    PyObject* retval;
    copy_str = strdup(orig_str);
    retval = (PyObject*) Py_BuildValue("ss",orig_str, reverse(copy_str));
    free(copy_str);
    return (retval);
}

static PyMethodDef ExtestMethods[] = {
    { "fac",Extest_fac,METH_VARARGS },
    { "doppe",Extdoppel,METH_VARARGS },
    { NULL,NULL },
};

void initExtest() {
    Py_InitModule("Extest",ExtestMethods);
}

当执行import Extest的时候,首先初始化initExtest
再关联到ExtestMethods,
其中的数组把函数Extest_fac关联到fac,Extdoppel关联到doppe。

好了,现在所有的源文件已经准备好了。

我们把它编译成动态库。

gcc -fPIC -shared extest1.c pytest1.c -o Extest.so -I /usr/include/python2.6/

其中的I 后面的路径是你Python.h头文件的路径。

进入python环境。

Extest.so文件中包含的Extest模块,现在可以像普通的python模块一样被引用了。

>>> from Extest import fac
>>> from Extest import doppe
>>> fac(4)
24
>>> doppe('hello world')
('hello world', 'dlrow olleh')
>>> 

但是如果工程比较复杂,直接用命令敲的方式就比较麻烦。工程管理可以使用Makefile。python也提供更简单的方式setup的方式。

setup.py文件内容如下:

#!/usr/bin/env python

from distutils.core import setup,Extension

setup( 
    name = 'Extest',
    version='1.0',
    ext_modules = [Extension('Extest',sources=['pytest1.c','extest1.c'],include_dirs=['/usr/include/python2.6/'])]
    )  

建立模块的包:

python setup.py build

就可以生成工程文件的目录了

python setup.py install

可以把库文件安装进python的库文件目录中 ,通过python,在任何目录下都可以使用该模块了。

使用SWIG为C自动生成python的拓展

以上介绍的是利用C语言的python API为python写拓展模块的方式,那有没有更简单的方式呢。SWIG可以自动为C语言的函数生成其他语言的拓展。首先安装swig工具(略)。

再编写如下文件extest1.i

/* example.i */          
%module Extest      //模块名       
%{                       
#include "extest1.h"     
%}                       

int fac (int n);         

char *reverse( char *s );

用swig工具根据example.i生成extest1.c的python接口文件。

swig -python extest.i

即可生成extest1_wrap.c(extest1.c的接口文件,名字根据extest1 而来)、Extest.py(名字根据文件里面的module 而来)。

在编译成动态库:

gcc -fPIC -shared extest1.c extest1_wrap.c  -o _Extest.so -I /usr/include/python2.6/

生成目标动态库文件 _Extest.so。

注意, 这里的 _Extest.so 名称是固定的。命名规则是:前面的下划线(python拓展模块的标准写法)+模块名。

也可以使用

python setup.py install 

方式安装模块。setup.py内容如下。

#!/usr/bin/env python                                                                                                   

from distutils.core import setup,Extension                                                                              

setup(                                                                                                                  
    name = 'Extest',                                                                                                    
    version='1.0',                                                                                                      
    ext_modules = [Extension('_Extest',sources=['extest1_wrap.c','extest1.c'],include_dirs=['/usr/include/python2.6/'])]
    )                                                                                                                          

现在,swig生成的动态库和自己手写的一样可以工作了。

只要C写好了函数,用swig自动就能生成python的模块了,这是不是简单一些呢?

但是,如果C模块中的参数是复杂类型,如结构体等,则需要特别注意一下,这块内容以后有时间在深入了解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值