当前位置: 首页 > 图灵资讯 > 行业资讯> C语言与Python如何相互调用

C语言与Python如何相互调用

发布时间:2025-04-06 15:42:41

Python多年来一直备受关注,占据了许多领域的地位,Web、大数据、人工智能、操作和维护都有它的形象,甚至图形界面也很流畅。即使是“full-stack”这个词刚出来的时候,似乎也是为了描述它。

虽然Python存在GIL问题,导致多线程无法充分利用多核,但后来的multiprocess可以从多过程的角度使用多核,甚至affinity可以绑定特定的CPU核,这个问题也得到了解决。虽然它基本上是一种完整的堆栈语言,但有时它可能会考虑与C语言混合,以提高效率。

混合编辑是计算机中不可避免的话题,涉及很多事情。技术、架构、团队状况、管理、客户等环节可能会对其产生影响。当我想到混合编辑的问题时,我会再次发布一个特别的讨论。本文只讨论python和C混合编辑的方法,大致如下(本文的背景是linux,可以与其他平台进行比较):

共享库

使用C语言编译生成共享库,然后python使用ctype库中的cdll打开共享库。

例如,C语言代码是

/*func.c*/
intfunc(inta)
{
returna*a;
}

python代码为

#!/usr/bin/envpython
#test_so.py
fromctypesimportcdll
importos
p=os.getcwd()+'/libfunc.so'
f=cdll.LoadLibrary(p)
printf.func(99)

测试如下:

$gcc-fPIC-sharedfunc.c-olibfunc.so
$./test_so.py
9801

subprocess

C语言设计一个完整的可执行文件,然后python通过subprocess执行可执行文件,本质上是fork+execve。

例如,C语言代码是

/*test.c*/
#include<stdio.h>
intfunc(inta)
{
returna*a;
}
intmain(intargc,char**argv)
{
intx;
sscanf(argv[1],"%d",&x);
printf("%d\n",func(x));
return0;
}

Python代码为

#!/usr/bin/envpython
#test_subprocess.py
importos
importsubprocess
subprocess.call([os.getcwd()+'/a.out','99'])

测试如下:

$gcctest.c-oa.out
$./test_subprocess.py
9801

python程序在C语言中运行

C语言使用popen/system或直接使用系统调用级fork+exec来运行python程序也是一种混合手段。

例如,Python代码如下:

#!/usr/bin/envpython
#test.py
importsys
x=int(sys.argv[1])
printx*x

C语言代码如下:

/*test.c*/
#include<stdio.h>
#include<stdlib.h>
intmain()
{
FILE*f;
chars[1024];
intret;
f=popen("./test.py99","r");
while((ret=fread(s,1,1024,f))>0){
fwrite(s,1,ret,stdout);
}
fclose(f);
return0;
}

测试如下:

$gcctest.c
$./a.out
9801

python支持C语言扩展

许多编程语言都支持C语言的扩展,这有两个原因:

(1)在语言设计之初,可以充分利用C语言现有的库进行大量扩展;

(2)C语言运行效率高。

python也不例外。从诞生之日起,许多图书馆都是用C语言编写的。python的C语言扩展涉及python的数据结构和C语言的对应性。扩展方法实际上是用C语言编写共享库,但共享库中的接口是标准的,可以被python识别。

为了解释如何扩展,我首先假设python下的函数功能,代码如下:

deffunc(*a):
res=1
foriinrange(len(a)):
res*=sum(a[i])
returnres

如上所示,所需的函数功能是参数是由任何多个数字组成的列表(其他数据结构暂时排除在外),并返回每个列表中元素之和的乘积。

先写python代码,如下所示:

#!/usr/bin/envpython
#test.py
importcolin
deffunc(*a):
res=1
foriinrange(len(a)):
res*=sum(a[i])
returnres
a=[1,2,3]
b=[4,5,6]
c=[7,8]
d=[9]
e=[10,11,12,13,14]
f=colin.func2(99)
g=colin.func3(a,b,c,d,e)
h=func3(a,b,c,d,e)
print"f=",f
print"g=",g
print"h=",h

带上之前测试过的平方func,相对简单。希望python写的func能和C语言扩展的结果一致。

首先,用C语言写下这些函数的实现,其中func3使用了一个数据结构y_,表示任何多个长数组。t,用x_t来表示单个数组。

/*colin.h*/
#ifndefColin_h
#defineColin_h
typedefstruct{
int*a;
intlen;
}x_t;
typedefstruct{
x_t*ax;
intlen;
}y_t;
intfunc2(inta);
intfunc3(y_t*p);
voidfree_y_t(y_t*p);
#endif
/*colin.c*/
#include"colin.h"
#include<stdlib.h>
intfunc2(inta)
{
returna*a;
}
intfunc3(y_t*p)
{
intresult;
intsum;
inti,j;
result=1;
for(i=0;i<p->len;i++){
sum=0;
for(j=0;j<p->ax[i].len;j++)
sum+=p->ax[i].a[j];
result*=sum;
}
returnresult;
}
voidfree_y_t(y_t*p)
{
inti;
for(i=0;i<p->len;i++){
free(p->ax[i].a);
}
free(p->ax);
}

上面定义了三个函数,func2代表平方,func3代表之前提到的功能,因为y_t结构可能是动态分配的,所以给出了一种归还内存的方法。

正如我刚才所说,如果python扩展,我们需要“标准化”共享库的界面。所以我们打包它,给它一个加载python的入口。

/*wrap.c*/
#include<Python.h>
#include<stdlib.h>
#include"colin.h"
PyObject*wrap_func2(PyObject*self,PyObject*args)
{
intn,result;
/*从参数列表中导出一个整形手术,使用"i"*/
if(!PyArg_ParseTuple(args,"i",&n))
returnNULL;
用C语言的库实现计算*/
result=func2(n);
计算结果必须导致python识别类型*/
returnPy_BuildValue("i",result);
}
PyObject*wrap_func3(PyObject*self,PyObject*args)
{
intn,result;
inti,j;
intsize,size2;
PyObject*p,*q;
y_t*y;
y=malloc(sizeof(y_t));
/*先数有多少参数,即列表的数量*/
size=PyTuple_Size(args);
*/*先分配数组的数量*/
y->len=size;
y->ax=malloc(sizeof(x_t)*size);
/*python中各列表(参数)*/
for(i=0;i<size;i++){
/*先获得第一个参数,一个列表*/
p=PyTuple_GetItem(args,i);
/*获取列表的长度*/
size2=PyList_Size(p);
/*为数组分配空间*/
y->ax[i].len=size2;
y->ax[i].a=malloc(sizeof(int)*size2);
/*遍历列表,依次将列表中的数字转移到数组中*/
for(j=0;j<size2;j++){
q=PyList_GetItem(p,j);
PyArg_Parse(q,"i",&y->ax[i].a[j]);
}
}
用C语言的库实现计算*/
result=func3(y);
free_y_t(y);
free(y);
/*结果转化为python识别格式*/
returnPy_BuildValue("i",result);
}
/*这是接口列表,加载时只加载列表的地址,所以这个数据结构不能放在栈(局部变量)内,*/
staticPyMethodDefcolinMethods[]=
{
{"func2",wrap_func2,METH_VARARGS,"Justatest"},
{"func3",wrap_func3,METH_VARARGS,"Justatest"},
{NULL,NULL,METH_NOARGS,NULL}
};
/*python加载时的界面*/
/*注意,既然库名叫colin,这个函数必须交给initcolin*//
voidinitcolin()
{
PyObject*m;
m=Py_InitModule("colin",colinMethods);
}

在这个过程中,我猜Pyarg_Vaparse应该功能更强大,但是反复测试没有成功,也没有仔细阅读文档。

测试一下

$gcc-I/usr/include/python2.7/-fPIC-sharedcolin.cwrap.c-ocolin.so
$./test.py
f=9801
g=729000
h=729000

C语言写的函数与python写的函数结果一致。

python培训视频众多,全部在python学习网,欢迎在线学习!

本文转自:https://www.jianshu.com/p/9ffc1534e588

相关文章

如何让vim支持python3

如何让vim支持python3

2025-09-12
python2.7和3.6区别有哪些

python2.7和3.6区别有哪些

2025-09-12
python3有serial库吗

python3有serial库吗

2025-09-12
python中w、r表示什么意思

python中w、r表示什么意思

2025-09-12
python中如何把list变成字符串

python中如何把list变成字符串

2025-09-12
python命名空间是什么

python命名空间是什么

2025-09-12