python调用c程序

python调用c程序

 

python语言可以调用c程序,其处理的基本流程如下:

创建c程序功能代码

一、创建c源程序文件py_test.c

这是程序的具体功能代码,也就是python需要调用的c源程序。

示例代码写了三个方法,最终的效果是python可以调用这三个方法。

/*
 * File      : py_test.c
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-09-22     dolphin      the first version
 */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>


/* 求阶乘的函数 */
int fac(int n){
    if(n < 2)
        return 1;
    return n*fac(n-1);
}

/* 字符串逆序的函数 */
/* 比如:输入abcdefg,返回gfedcba */
char *reverse(char *s) {
    char t,*p = s ,*q = (s+strlen(s)-1);
    while(s && (p<q)) {
        t = *p;
        *p++ = *q;
        *q-- = t;
    }
    return s;
}
/* 用公式计算pi的函数,对应公式如下:
   pi/2 = 1+1/3+1/3*2/5 + 1/3*2/5*3/7 + 1/3*2/5*3/7*4/9+......
*/
char *pi_fun(int len, char *ans)
{
    int i;                           //len为小数长度
    int numberator = 1,denominator = 3,result,carry;
    int flag = 1,count = 0;          //继续循环的标志及循环的次数
    char *pi,*temp;                  //指向保存pi值和临时计算结果的数据
    len += 2;                       //增加两位
    printf("len = %d\n",len);
    pi = (char*)malloc(sizeof(char)*(len+1));   //分配保存pi值的内存
    temp = (char*)malloc(sizeof(char)*len);     //分配保存呢临时值的内存
    for(i = 0; i < len; i++)                      //初始化数组
    {
        pi[i] = temp[i] = 0;
    }
    pi[1] = 2;                                     //置初值
    temp[1] = 2; 
    while(flag && (++count < 2147483647))          //int的最大值 2147483647
    {
        carry = 0;
        for(i = len-1; i > 0; i--)                 //从低位到高位相乘
        {
            result = temp[i] * numberator + carry; //用每一位去乘,再加上进位
            temp[i] = result % 10;               //保存个数
            carry = result / 10;                 //进位
        }
 
        carry = 0;
        for(i = 0; i < len; i++)                 //有高位到低位进行除法运算
        {
            result = temp[i] + carry*10;         //当前位加上前一位的余数
            temp[i] = result / denominator;      //当前位的整数部分
            carry = result % denominator;        //当前位的余数,累加到下一位的运算
        }
        flag = 0;                                 //清除标志
        for(i = len-1; i > 0; i--)               
        {   
            result = pi[i] + temp[i];            //将计算结果累加到result中
            pi[i] = (result % 10);               //保留一位
            pi[i-1] += result / 10;              //向高位进位
            flag |= temp[i];                     //若temp中的数全为0,退出循环
        } 
        numberator++;                            //累加分子
        denominator += 2;                        //累加分母
    }
    for(i = 0; i < len; i++)               
    {   
        pi[i] = pi[i] + '0';
    }
    pi[0]= pi[1];
    pi[1]= '.';
    pi[len] = '\0';
    printf("\n计算了%d次\n",count);              //输出循环次数
    strcpy(ans, pi);
    free(pi);
    free(temp);    
    return ans;
}

/* test函数 */
int test(void)
{
    return 0;
}

二、创建.h头文件py_test.h

完成功能函数的头文件,声明了py_test.c中的功能函数,方便调用。

#ifndef PY_TEST_H_
#define PY_TEST_H_

char *pi_fun(int len, char *ans);
int fac (int n) ;
char *reverse(char *s) ;
int test(void) ;

#endif

python类型适配

一、写wrapper文件py_testwrapper.c

这个wrapper文件也是一段c语言代码,可以参考官方的文件Extending Python with C or C++,步骤如下:

1、包含Python.h这个头文件,这个头文件在python安装目录下的include目录下找到。

2、包含py_test.h这个头文件,这个就是创建的功能函数的头文件。

3、给py_test.c这个源程序中的每个方法都要设置一个wrapper函数,它以PyObject为返回值,并且每个函数都有两个参数PyObject *self和PyObject *args,用来传入数据。

4、添加PyMethodDef,用来定义方法名以及方法名与wrapper函数的对应关系。

5、添加PyModuleDef,用来定义模块名和该模块所具有的方法。

6、在PyInit_fun中通过PyModule_Create函数创建模块。

下面就是这个wrapper文件的完整代码:

/*
 * File      : py_testwrapper.c
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-09-22     dolphin      the first version
 */
#include <Python.h>
#include <stdlib.h>
#include <string.h>
#include "py_test.h"

/**int PyArg_ParseTuple(PyObject *arg, const char *format, ...);**/

/* fac功能的wrapper函数 */
static PyObject *py_test_fac(PyObject *self, PyObject *args)
{
    int num;
    int ans;
    PyObject *retval;
    //int ok = PyArg_ParseTuple(args, ""); /* No arguments */     /* Python call: f() */
    //int ok = PyArg_ParseTuple(args, "s", &s); /* A string */     /* Python call: f('whoops!') */
    //int ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);/* A pair of ints and a string, whose size is also returned */
                                                                /* Python call: f((1, 2), 'three') */
    //const char *file;
    //const char *mode = "r";
    //int bufsize = 0;
    //int ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);/* A string, and optionally another string and an integer */
                                                                /* Python calls:f('spam')、f('spam', 'w')、f('spam', 'wb', 100000) */ 
    //int left, top, right, bottom, h, v;
    //int ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",&left, &top, &right, &bottom, &h, &v);
                                                                /* A rectangle and a point */
                                                                /* Python call:f(((0, 0), (400, 300)), (10, 10))*/
    //Py_complex c;
    //int ok = PyArg_ParseTuple(args, "D:myfunction", &c);
                                                                /* a complex, also providing a function name for errors */
                                                                /* Python call: myfunction(1+2j) */                                                                
    //按整形"i"获得传入的整形数据,存入num
    if (!PyArg_ParseTuple(args,"i",&num)) 
        return NULL;
    //调用fac,求阶乘
    ans = fac(num);
    //按整形"i"将结果装入retval
    retval = (PyObject *)Py_BuildValue("i", ans);
    return retval;
}


/* reverse功能的wrapper函数,因python中有reverse函数,使用doppel替代 */
static PyObject *py_test_doppel(PyObject *self, PyObject *args)
{
    char *src;
    char *mstr;
    PyObject *retval;

    //按字符串"s"获得传入的整形数据,存入src
    if (!PyArg_ParseTuple(args,"s",&src))
        return NULL;
    //申请存储空间
    mstr = malloc(strlen(src) + 1);
    //拷贝src到mstr
    strcpy(mstr,src);
    //调用reverse方法,逆序字符串
    reverse(mstr);
    //按字符串"ss"将两个字符串装入retval
    retval = (PyObject *)Py_BuildValue("ss",src,mstr);
    //释放空间
    free(mstr);
    return retval;
}

/* pi功能的wrapper函数 */
static PyObject *py_test_pi(PyObject *self, PyObject *args)
{
    char *mstr;
    int num ;
    //int result;
    PyObject *retval;

    //按整形"i"获得传入的整形数据,存入num
    if (!PyArg_ParseTuple(args,"i",&num)) 
        return NULL;
    //申请存储空间
    mstr = (char*)malloc(sizeof(char)*(num + 3));
    //调用pi_fun方法
    pi_fun(num, mstr);
    //按字符串"s"将结果装入retval
    retval = (PyObject *)Py_BuildValue("s",mstr);
    //释放空间
    free(mstr);
    return retval;
}

/* test功能的wrapper函数 */
static PyObject *py_test_test(PyObject *self,PyObject *args)
{
    PyObject *retval;
    //调用test方法
    test();
    retval = (PyObject *)Py_BuildValue("");
    return retval;
}

/* 将上述封装的wrapper函数添加到PyMethodDef中 */
static PyMethodDef py_testMethods[] = {
    {"fac",py_test_fac,METH_VARARGS},
    {"doppel",py_test_doppel,METH_VARARGS},
    {"test",py_test_test,METH_VARARGS},
    {"pi",py_test_pi,METH_VARARGS},
    {NULL,NULL},
};

#if 0
/* python2对应的初始化python模块的方法 */
void initpy_test(void)
{
    Py_InitModule("py_test",py_testMethods);
}
#else
/* python3对应的初始化python模块的方法 */
static struct PyModuleDef pytestmodule = {
    PyModuleDef_HEAD_INIT,
    "py_test",   /* name of module */
    NULL, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    py_testMethods
};

/* The initialization function must be named PyInit_name(), 
   where name is the name of the module, 
   and should be the only non-static item defined in the module file */
PyMODINIT_FUNC PyInit_py_test(void)
{
    return PyModule_Create(&pytestmodule);
}
#endif

编译、安装

一、编译

创建setup.py文件,以便使用python的自带模块进行

#incoding:utf-8
from distutils.core import setup,Extension
#模块名
MOD = 'py_test'
#资源(要编译和链接的代码文件)
source = ['py_test.c','py_testwrapper.c']

#调用setup函数,编译和链接
setup(name=MOD,ext_modules=[Extension(MOD,sources=source)])

打开终端命令行窗口,进入文件目录,输入“python3 setup.py build”来编译(可能需要根据提示安装对应操作系统的工具链)。

二、安装

编译完成过后,当前文件目录会出现“build”文件夹,里面就是编译后的内容。

编译完成后我们还要将它们安装到python的库中,可以通过命令“sudo python3 setup.py install”来安装。

即将可执行的功能模块安装到了python库中,在python安装目录下Lib目录的site-packages文件夹下。

以后便可通过“import”将模块导入,使用模块中的具体功能。

测试

  以下在python中来调用我们自己的c程序进行测试,测试程序testpython.py如下:

#incoding:utf-8
import py_test
print("-"*50)
print(py_test.fac(10)) #调用fac()求阶乘的函数
print(py_test.doppel("This is my world"))#调用逆序函数
print("-"*50)

运行程序"python3 testpython.py",程序将运算的结果打印在终端窗口中:

我们有一个用C语言实现的运算圆周率任意位小数的模块,为了比较运算速度,用python实现同样功能的python模块"pi_fun.py"如下,其中py3是与C语言完全对应的python函数,其它两种算法("pi"和"pi2")会涉及大数运算,并未直接拿来比较:

import time
import numpy as np

class pi_fun(object):
    def pi(places=10):
    # 3 + 3*(1/24) + 3*(1/24)*(9/80) + 3*(1/24)*(9/80)*(25/168)
    # The numerators 1, 9, 25, ... are given by (2x + 1) ^ 2
    # The denominators 24, 80, 168 are given by (16x^2 -24x + 8)
        extra = 8
        one = 10 ** (places+extra)
        t, c, n, na, d, da = 3*one, 3*one, 1, 0, 0, 24

        while t > 1: 
            n, na, d, da = n+na, na+8, d+da, da+32
            t = t * n // d
            c += t
        return c // (10 ** extra)

    def pi_t(n=10):
        #t1 = time.ticks_us()
        t = pi(n)
        #t2 = time.ticks_us()
        #print('elapsed: ', time.ticks_diff(t2,t1)/1000000, 's')
        return t

    def pi2(n=10):
        r = 6 * (10 ** n) * 1000
        p = 0
        k = 0
        c = r // 2
        d = c // (2 * k + 1)
        while d > 0:
            p = p + d
            k = k + 1
            k2 = 2 * k
            c = c * (k2 - 1) // (4 * k2)
            d = c // (k2 + 1)
        return p // 1000

    def pi2_t(n=10):
        #t1 = time.ticks_us()
        t = pi2(n)
        #t2 = time.ticks_us()
        #print('elapsed: ', time.ticks_diff(t2,t1)/1000000, 's')
        return t
    
    def pi3(n =10):
        numberator = 1
        denominator = 3
        result = 0
        carry = 0
        flag = 1
        count = 0
        len = n + 2;   #增加两位
        pi =  [0 for x in range(0, len+1)]#(char*)malloc(sizeof(char)*(len+1));   //分配保存pi值的内存
        temp = [0 for x in range(0, len+1)]#(char*)malloc(sizeof(char)*len); //分配保存呢临时值的内存
        pi[1] = 2;#置初值
        temp[1] = 2; 
        while flag > 0 and  count < 2147483647:  #int的最大值 2147483647
            count = count + 1
            carry = 0;
            for j in range(0, len-1):
            #for i = len-1; i > 0; i--)     #从低位到高位相乘
                result = temp[len -1 - j] * numberator + carry; #用每一位去乘,再加上进位
                temp[len - 1 - j] = result % 10;          #保存个数
                carry = result // 10;                 #进位
            carry = 0;
            for i in range(0, len):
            #for(i = 0; i < len; i++)                     #有高位到低位进行除法运算
                result = temp[i] + carry * 10;             #当前位加上前一位的余数
                temp[i] = result // denominator;          #当前位的整数部分
                carry = result % denominator;            #当前位的余数,累加到下一位的运算
            flag = 0;                                     #清除标志
            for j in range(0, len-1):
            #for(i = len-1; i > 0; i--)               
            #{   
                result = pi[len - 1 - j] + temp[len - 1 - j];  #将计算结果累加到result中
                pi[len - 1 - j] = (result % 10);               #保留一位
                                
                pi[len - 1 - j - 1] = pi[len - 1 - j - 1] + result // 10;            #向高位进位
                flag = flag | temp[len - 1 - j];               #若temp中的数全为0,退出循环
            #} 
            numberator = numberator + 1#累加分子
            denominator = denominator + 2#累加分母

        pi_ans = ''
        pi_ans += str(pi[1])
        pi_ans += '.'    
        for i in range(2, len):               
            pi_ans += str(pi[i])        
        return pi_ans#
    
if __name__ == "__main__":
        try:
                cal = pi_fun
                print(cal.pi3(10))
        finally:
                print('end fun')   

接下来就是为测试函数增加运算时间计算的显示,修改的"testpython.py"代码如下:

#incoding:utf-8
import py_test
import time
import numpy as np
from pi_fun import pi_fun


if __name__ == "__main__":
    print("-"*50)
    print(py_test.fac(10)) #调用fac()求阶乘的函数
    print(py_test.doppel("This is my world"))#调用逆序函数
    start = time.time()
    k = py_test.pi(1000)
    t1 = time.time() - start
    print(t1)
    print(k)
    print("-"*50)
    cal = pi_fun
    start = time.time()
    k = cal.pi3(1000)
    t2 = time.time() - start
    print(t2)
    print(k)
    print("-"*50)
    print(t2/t1)

运算结果如图所示,两者运算1000位圆周率小数结果完全一样,但消耗的时间有56倍差异:

 

编辑于 2018-09-22

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值