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