python3调用及封装并调用c程序
首先,我们来看一个示例:
导入c程序实现:
import time
from ctypes import *
def main():
num = int(input("请输入整数值:"))
result = 0
start_time = time.time()
result = cdll.LoadLibrary("./add.so")
result.my_add(num)
end_time = time.time()
print("总共用时%s"%(end_time-start_time))
if __name__ == "__main__":
main()
纯python实现
import time
def main():
num = int(input("请输入整数值:"))
result = 0
start_time = time.time()
for i in range(num+1):
result += i
print("从1到%d累加的计算结果为%d"%(num,result))
end_time = time.time()
print("总共用时%s"%(end_time-start_time))
if __name__ == "__main__":
main()
执行结果对比
[root@bogon test1]# python3 c_add.py
请输入整数值:200
从1到200累加的计算结果为20100
总共用时0.00023293495178222656
[root@bogon test1]# python3 python_add.py
请输入整数值:200
从1到200累加的计算结果为20100
总共用时7.748603820800781e-05
[root@bogon test1]# vim c_add.py
[root@bogon test1]# vim python_add.py
可以很清晰的看到 c运算的时间和python运算时间对比结果。
c与python对比:python不擅长"大量运算"任务,python程序优势在于:编写简单,适合"IO密集型作业"(比如打开文件,下载图片,运行脚本)。python快速实现"计算密集型作业"的方法就是"把c的代码拿过来直接用"。
一、python3直接调用c程序
上边第一个代码就是python3直接调用c程序的示例,步骤一共分四步,让我们一起实现一下:
四步:
1.将.c后缀的文件编译为动态库文件(.so结尾)
gcc 原文件名.c -shared -o 新文件名.so
或者
gcc 原文件名.c -shared -o test.so -I/usr/include/python3.6m -fPIC -lpython3
示例
以将test.c编译为test.so为例
gcc test.c -shared -o test.so
2.在python文件中导入头文件
from ctypes import *
3.在python中引入c动态库,并用变量接收动态库的引用
格式:
变量名 = cdll.LoadLibrary("动态库文件路径")
示例:
result = cdll.LoadLibrary("./add.so")
4.调用动态库方法
格式:
动态库引用.库函数(参数)
示例:
result.my_add(num)
实现示例:
1.准备c程序add.c
#include <stdio.h>
void my_add(int num) {
long int result = 0;
for(long int i = 1; i <= num; i++) {
result += i;
}
printf("从1到%d累加的计算结果为%ld\n",num,result);
}
编译成 so库文件:
gcc add.c -shared -o add.so
2.python3代码实现:
import time
#引入
from ctypes import *
def main():
num = int(input("请输入整数值:"))
result = 0
start_time = time.time()
#直接导入库文件
result = cdll.LoadLibrary("./add.so")
#调用方法
result.my_add(num)
end_time = time.time()
print("总共用时%s"%(end_time-start_time))
if __name__ == "__main__":
main()
执行python3 c_add.py
[root@bogon test1]# python3 c_add.py
请输入整数值:200
从1到200累加的计算结果为20100
总共用时0.00023293495178222656
二、python3封装后以模块的形式调用c程序
我们来以一个小demo为例,这里首先展示下demo目录结构
demo
-- install.sh # sh构建文件
-- main.py # 测试文件
-- setup.py # 构建py扩展库文件
-- add.c # C扩展库
install.sh
python3 setup.py install
rm -rf ./dist
rm -rf ./build
setup.py
封装启动文件
from distutils.core import setup, Extension
setup(name='aaa', # 定义的模块名
version='1.0',
ext_modules=[
Extension('aaa', ['./add.c']) # aaa为c文件中定义的模块名,add.c为c文件路径
]
)
test.c
简单的fib函数封装,以下内容为python3的封装方式,python2可自行百度
#include <Python.h>
//a func to calc fib numbers
int cFib(int n)
{
if (n<2) return n;
return cFib(n-1) + cFib(n-2);
}
// 实现c与py参数返回值桥接
static PyObject* fib(PyObject* self,PyObject* args)
{
int n;
if (!PyArg_ParseTuple(args,"i",&n))
return NULL;
return Py_BuildValue("i",cFib(n));
}
// 定义模块包含的功能/函数
static PyMethodDef module_methods[] = {
{"fib",(PyCFunction) fib, METH_VARARGS,"calculates the fibonachi number"},
{NULL,NULL,0,NULL}
};
// 封装一个模块结构体
static struct PyModuleDef aaa =
{
PyModuleDef_HEAD_INIT,
"aaa", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
module_methods
};
// init该模块为py模块
PyMODINIT_FUNC PyInit_aaa(void)
{
return PyModule_Create(&aaa);
}
main.py
用于测试c扩展库是否构建成功
import aaa
if __name__ == '__main__':
for num in range(10):
print(aaa.fib(num))
需要注意的是,你期待生成的模块名需与c文件中几个关键的名称须保持一致,PyModuleDef结构体里,setup里
以上文件准备好后,进入目录进行测试
执行构建sh文件
[root@bogon test2]# ./install.sh
running install
running build
running build_ext
building 'aaa' extension
creating build
creating build/temp.linux-loongarch64-3.6
loongarch64-loongson-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -g -D_GNU_SOURCE -fPIC -fwrapv -O2 -g -D_GNU_SOURCE -fPIC -fwrapv -O2 -g -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python3.6m -c ./add.c -o build/temp.linux-loongarch64-3.6/./add.o
creating build/lib.linux-loongarch64-3.6
loongarch64-loongson-linux-gnu-gcc -pthread -shared -Wl,-z,relro -Wl,-z,now -g -Wl,-z,relro -Wl,-z,now -g build/temp.linux-loongarch64-3.6/./add.o -L/usr/lib64 -lpython3.6m -o build/lib.linux-loongarch64-3.6/aaa.cpython-36m-loongarch64-linux-gnu.so
running install_lib
copying build/lib.linux-loongarch64-3.6/aaa.cpython-36m-loongarch64-linux-gnu.so -> /usr/local/lib64/python3.6/site-packages
running install_egg_info
Removing /usr/local/lib64/python3.6/site-packages/aaa-1.0-py3.6.egg-info
Writing /usr/local/lib64/python3.6/site-packages/aaa-1.0-py3.6.egg-info
不报错即构建成功
测试:
[root@bogon test2]# python3 main.py
0
1
1
2
3
5
8
13
21
34
如果执行测试脚本时提示导入模块问题,例:
ImportError:The dynamic module does not define the init function
请检查 期待生成的模块名需与c文件中几个关键的名称须保持一致,即以上例子中setup.py和test.c中所有为aaa的地方
两个c和头文件的情况下的封装
c程序test_add.c
#include "test.h"
int add(int a,int b)
{
return a+b;
}
头文件test.h
int add(int a,int b);
c的扩展程序test_add_py.c,只适用于python3
#include <Python.h>
#include "test.h"
PyObject* wrap_add(PyObject* self, PyObject* args)
{
int n0,n1, result;
if (!PyArg_ParseTuple(args, "ii", &n0, &n1))
return NULL;
printf("n0 = %d\n",n0);
printf("n1 = %d\n",n1);
result = add(n0,n1);
return Py_BuildValue("i", result);
}
static PyMethodDef testMethods[] =
{
{"add", wrap_add, METH_VARARGS, "Add"},
{NULL, NULL}
};
static struct PyModuleDef test = {
PyModuleDef_HEAD_INIT,
"test",
NULL,
-1,
testMethods};
PyMODINIT_FUNC PyInit_test(void)
{
return PyModule_Create(&test);
}
编译
gcc -fPIC -shared -o test.so -I/usr/include/python3.6m test_add.c test_add_py.c -lpython3
测试main.py
import test
if __name__ == '__main__':
print(test.add(2,4))
执行
[root@bogon test2]# python3 main.py
n0 = 2
n1 = 4
6
引用#include <Python.h>需要安装python3-devel