浅谈python和C语言混编的几种方式(推荐)


Posted in Python onSeptember 27, 2017

Python这些年风头一直很盛,占据了很多领域的位置,Web、大数据、人工智能、运维均有它的身影,甚至图形界面做的也很顺,乃至full-stack这个词语刚出来的时候,似乎就是为了描述它。

Python虽有GIL的问题导致多线程无法充分利用多核,但后来的multiprocess可以从多进程的角度来利用多核,甚至affinity可以绑定具体的CPU核,这个问题也算得到解决。虽基本为全栈语言,但有的时候为了效率,可能还是会去考虑和C语言混编。混编是计算机里一个不可回避的话题,涉及的东西很多,技术、架构、团队情况、管理、客户等各个环节可能对其都有影响,混编这个问题我想到时候再开一贴专门讨论。本文只讲python和C混编的方式,大致有如下几种方式(本文背景是linux,其他平台可以类比):

共享库

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

举例如下,C语言代码为

/* func.c */int func(int a)
{
 return a*a;
} 

python代码为

#!/usr/bin/env python
#test_so.pyfrom ctypes import cdll
import os

p = os.getcwd() + '/libfunc.so'
f = cdll.LoadLibrary(p)
print f.func(99)

测试如下

$ gcc -fPIC -shared func.c -o libfunc.so 
$ ./test_so.py 
9801

subprocess

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

举例如下,C语言代码为

/* test.c */
#include <stdio.h>
int func(int a)
{
 return a*a;
}

int main(int argc, char **argv)
{
 int x;

 sscanf(argv[1], "%d", &x);
 printf("%d\n", func(x));
 return 0;
}

Python代码为

#!/usr/bin/env python
# test_subprocess.py
import os
import subprocess

subprocess.call([os.getcwd()+'/a.out', '99'])

测试如下

$ gcc test.c -o a.out
$ ./test_subprocess.py
9801

C语言中运行python程序

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

举例如下,Python代码如下

#!/usr/bin/env python
# test.py
import sys
x = int(sys.argv[1])
print x*x

C语言代码如下

/* test.c */
#include <stdio.h>
#include <stdlib.h>
int main()
{
 FILE *f;
 char s[1024];
 int ret;

 f = popen("./test.py 99", "r");
 while((ret=fread(s,1,1024,f))>0) {
  fwrite(s,1,ret,stdout);
 }
 fclose(f);
 return 0;
}

测试如下

$ gcc test.c 
$ ./a.out
9801

python对C语言扩展的支持

很多编程语言都为C语言扩展添加了支持,这有两种原因:(1)语言设计之初,可以充分的利用C语言已有的库来做很多扩展;(2)C语言的运行效率高。

python也不例外,从诞生那天起,很多库都是C语言写的。python的C语言扩展中涉及到python的数据结构与C语言的对应,扩展方法其实是用C语言编写一个共享库,只是这个共享库中的接口是一个规范的,可以被python识别的。

为了说明如何扩展,我这里先假设一个在python下的函数功能,代码如下

def func(*a):
 res=1
 for i in range(len(a)):
 res *= sum(a[i])
 return res

如上,希望的函数功能是,参数是任意多个数字组成的列表(姑且排除其他数据结构),返回每个列表的元素之和的乘积。

姑且先把python代码写了,如下所示

#!/usr/bin/env python
# test.py
import colin

def func(*a):
 res=1
 for i in range(len(a)):
 res *= sum(a[i])
 return res

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 */
#ifndef Colin_h
#define Colin_h
typedef struct {
 int *a;
 int len;
} x_t;
typedef struct {
 x_t *ax;
 int len;
} y_t;
int func2(int a);
int func3(y_t *p);
void free_y_t(y_t *p);
#endif
/* colin.c */
#include "colin.h"
#include <stdlib.h>

int func2(int a)
{
 return a*a;
}

int func3(y_t *p)
{
 int result;
 int sum;
 int i, 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;
 }

 return result;
}

void free_y_t(y_t *p)
{
 int i;
 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)
{
 int n, result;
 /* 从参数列表中导出一个整形,用"i" */
 if (!PyArg_ParseTuple(args, "i", &n))
  return NULL;

 /* 用C语言的库实现来计算 */
 result = func2(n);
 /* 计算结果必须要导成python识别的类型 */
 return Py_BuildValue("i", result);
}

PyObject* wrap_func3(PyObject* self, PyObject* args)
{
 int n, result;
 int i, j;
 int size, 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++) {
  /* 先获得第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识别格式 */
 return Py_BuildValue("i", result);
}

/* 这是接口列表,加载时是只加载此列表的地址,所以这个数据结构不能放栈(局部变量)内,会被清掉 */
static PyMethodDef colinMethods[] =
{
 {"func2", wrap_func2, METH_VARARGS, "Just a test"},
 {"func3", wrap_func3, METH_VARARGS, "Just a test"},
 {NULL, NULL, METH_NOARGS, NULL}
};

/* python加载的时候的接口 */
/* 注意,既然库名叫colin,此函数必须交initcolin */
void initcolin()
{
 PyObject *m;
 m = Py_InitModule("colin", colinMethods);
}

过程中,我猜测PyArg_VaParse应该功能更为强大,可是反复测没有成功,也没细看文档。

测试一下

$ gcc -I /usr/include/python2.7/ -fPIC -shared colin.c wrap.c -o colin.so 
$ ./test.py 
f = 9801 
g = 729000 
h = 729000

可以看到,C语言写的函数和python写的函数结果一致。

以上这篇浅谈python和C语言混编的几种方式(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python自然语言编码转换模块codecs介绍
Apr 08 Python
python中lambda()的用法
Nov 16 Python
Python使用requests及BeautifulSoup构建爬虫实例代码
Jan 24 Python
Python实现通讯录功能
Feb 22 Python
python实现基于信息增益的决策树归纳
Dec 18 Python
Python编程中flask的简介与简单使用
Dec 28 Python
python 动态迁移solr数据过程解析
Sep 04 Python
详解字符串在Python内部是如何省内存的
Feb 03 Python
Python3操作MongoDB增册改查等方法详解
Feb 10 Python
tensorflow的ckpt及pb模型持久化方式及转化详解
Feb 12 Python
Python3将ipa包中的文件按大小排序
Apr 17 Python
Pycharm常用快捷键总结及配置方法
Nov 14 Python
解决Python字典写入文件出行首行有空格的问题
Sep 27 #Python
Python基于分水岭算法解决走迷宫游戏示例
Sep 26 #Python
Python计算斗牛游戏概率算法实例分析
Sep 26 #Python
Python使用修饰器执行函数的参数检查功能示例
Sep 26 #Python
浅谈python for循环的巧妙运用(迭代、列表生成式)
Sep 26 #Python
Python开发中爬虫使用代理proxy抓取网页的方法示例
Sep 26 #Python
Python3.4实现从HTTP代理网站批量获取代理并筛选的方法示例
Sep 26 #Python
You might like
php源代码安装常见错误与解决办法分享
2013/05/28 PHP
变量在 PHP7 内部的实现(二)
2015/12/21 PHP
PHP的邮件群发系统phplist配置方法详细总结
2016/03/30 PHP
翻译整理的jQuery使用查询手册
2007/03/07 Javascript
Prototype1.6 JS 官方下载地址
2007/11/30 Javascript
input、button的不同type值在ajax提交表单时导致的陷阱
2009/02/24 Javascript
让mayfish支持mysqli数据库驱动的实现方法
2010/05/22 Javascript
jQuery jcrop插件截图使用方法
2013/11/20 Javascript
JavaScript数据结构与算法之集合(Set)
2016/01/29 Javascript
jQuery fancybox在ie浏览器下无法显示关闭按钮的解决办法
2016/02/19 Javascript
jquery判断类型是不是number类型的实例代码
2016/10/07 Javascript
AngularJS入门教程之过滤器用法示例
2016/11/02 Javascript
javascript九宫格图片随机打乱位置的实现方法
2017/03/15 Javascript
vue实现页面加载动画效果
2017/09/19 Javascript
Vuejs+vue-router打包+Nginx配置的实例
2018/09/20 Javascript
在Vant的基础上实现添加表单验证框架的方法示例
2018/12/05 Javascript
Vue和React组件之间的传值方式详解
2019/01/31 Javascript
vue仿ios列表左划删除
2019/09/26 Javascript
python笔记(2)
2012/10/24 Python
在Python中使用M2Crypto模块实现AES加密的教程
2015/04/08 Python
基于Python代码编辑器的选用(详解)
2017/09/13 Python
python 打印直角三角形,等边三角形,菱形,正方形的代码
2017/11/21 Python
python使用Plotly绘图工具绘制柱状图
2019/04/01 Python
Python八皇后问题解答过程详解
2019/07/29 Python
python线程的几种创建方式详解
2019/08/29 Python
Python中的延迟绑定原理详解
2019/10/11 Python
python中for循环变量作用域及用法详解
2019/11/05 Python
Python数据分析pandas模块用法实例详解
2019/11/20 Python
python中sklearn的pipeline模块实例详解
2020/05/21 Python
详解基于python的图像Gabor变换及特征提取
2020/10/26 Python
python线程优先级队列知识点总结
2021/02/28 Python
秋季红领巾广播稿
2014/01/27 职场文书
致200米运动员广播稿
2014/02/06 职场文书
计算机相关专业自荐信
2014/07/02 职场文书
2014年妇委会工作总结
2014/12/10 职场文书
道歉信怎么写
2015/05/12 职场文书