浅谈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调用Moxa PCOMM Lite通过串口Ymodem协议实现发送文件
Aug 15 Python
Python itertools模块详解
May 09 Python
Linux下将Python的Django项目部署到Apache服务器
Dec 24 Python
python3处理含有中文的url方法
May 10 Python
python实现旋转和水平翻转的方法
Oct 25 Python
Python基于mysql实现学生管理系统
Feb 21 Python
很酷的python表白工具 你喜欢我吗
Apr 11 Python
Python使用统计函数绘制简单图形实例代码
May 15 Python
python如果快速判断数字奇数偶数
Nov 13 Python
Python中的wordcloud库安装问题及解决方法
May 27 Python
详解selenium + chromedriver 被反爬的解决方法
Oct 28 Python
python模拟点击玩游戏的实例讲解
Nov 26 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
使ecshop模板中可引用常量的实现方法
2011/06/02 PHP
PHP中auto_prepend_file与auto_append_file用法实例分析
2014/09/22 PHP
PHP+百度AI OCR文字识别实现了图片的文字识别功能
2019/05/08 PHP
JQuery 插件模板 制作jquery插件的朋友可以参考下
2010/03/17 Javascript
JavaScript 设计模式之组合模式解析
2010/04/09 Javascript
基于jquery的划词搜索实现(备忘)
2010/09/14 Javascript
使用js修改客户端注册表的方法
2013/08/09 Javascript
jquery索引在使用中的一些困惑
2013/10/24 Javascript
Jquery模仿Baidu、Google搜索时自动补充搜索结果提示
2013/12/26 Javascript
Javascript 运动中Offset的bug解决方案
2014/12/24 Javascript
javascript制作2048游戏
2015/03/30 Javascript
配置Grunt的Task时通配符支持和动态生成文件名问题
2015/09/06 Javascript
深入剖析JavaScript中的函数currying柯里化
2016/04/29 Javascript
jQuery基于函数重载实现自定义Alert函数样式的方法
2016/07/27 Javascript
详解vue-validator(vue验证器)
2017/01/16 Javascript
详解jquery validate实现表单验证 (正则表达式)
2017/01/18 Javascript
EasyUi 打开对话框后控件赋值及赋值后不显示的问题解决办法
2017/01/19 Javascript
Bootstrap BootstrapDialog使用详解
2017/02/17 Javascript
jquery实现数字输入框
2017/02/22 Javascript
nodejs入门教程四:URL相关模块用法分析
2017/04/24 NodeJs
JavaScript运动框架 多值运动(四)
2017/05/18 Javascript
微信小程序实现图片懒加载的示例代码
2017/12/13 Javascript
JavaScript学习总结(一) ECMAScript、BOM、DOM(核心、浏览器对象模型与文档对象模型)
2018/01/07 Javascript
在vue中使用vue-echarts-v3的实例代码
2018/09/13 Javascript
我要点爆”微信小程序云开发之项目建立与我的页面功能实现
2019/05/26 Javascript
在JavaScript中使用严格模式(Strict Mode)
2019/06/13 Javascript
vue动态绘制四分之三圆环图效果
2019/09/03 Javascript
Python实现批量压缩图片
2018/01/25 Python
Windows下安装Scrapy
2018/10/17 Python
pandas 空数据处理方法详解
2019/11/02 Python
使用Tkinter制作信息提示框
2020/02/18 Python
Python使用正则表达式实现爬虫数据抽取
2020/08/17 Python
党的群众路线教育学习材料
2014/05/12 职场文书
上班离岗检讨书
2014/09/10 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
开网店计划分析
2019/07/30 职场文书