浅谈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 pygame安装过程笔记
Jun 05 Python
Python进阶之尾递归的用法实例
Jan 31 Python
python3+PyQt5使用数据库窗口视图
Apr 24 Python
python 筛选数据集中列中value长度大于20的数据集方法
Jun 14 Python
python3 拼接字符串的7种方法
Sep 12 Python
利用python修改json文件的value方法
Dec 31 Python
python 动态生成变量名以及动态获取变量的变量名方法
Jan 20 Python
Django框架实现分页显示内容的方法详解
May 10 Python
如何用Python做一个微信机器人自动拉群
Jul 03 Python
使用python实现下载我们想听的歌曲,速度超快
Jul 09 Python
python中time、datetime模块的使用
Dec 14 Python
PyQt5 QThread倒计时功能的实现代码
Apr 02 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/06/08 PHP
php使用strtotime和date函数判断日期是否有效代码分享
2013/12/25 PHP
php保存任意网络图片到服务器的方法
2015/04/14 PHP
优化WordPress中文章与评论的时间显示
2016/01/12 PHP
PHP实现将几张照片拼接到一起的合成图片功能【便于整体打印输出】
2017/11/14 PHP
PHP实现模拟http请求的方法分析
2017/12/20 PHP
Yii2压缩PHP中模板代码的输出问题
2018/08/28 PHP
js获取下拉列表框中的value和text的值示例代码
2014/01/11 Javascript
用js判断是否为360浏览器的实现代码
2015/01/15 Javascript
基于BootStrap Metronic开发框架经验小结【七】数据的导入、导出及附件的查看处理
2016/05/12 Javascript
微信小程序 时间格式化(util.formatTime(new Date))详解
2016/11/16 Javascript
JS 拦截全局ajax请求实例解析
2016/11/29 Javascript
setTimeout学习小结
2017/02/08 Javascript
js编写选项卡效果
2017/05/23 Javascript
微信小程序下拉刷新PullDownRefresh的使用方法
2018/11/29 Javascript
js实现图片放大并跟随鼠标移动特效
2019/01/18 Javascript
vue elementUI使用tabs与导航栏联动
2019/06/21 Javascript
[01:01:51]EG vs VG Supermajor小组赛B组 BO3 第二场 6.2
2018/06/03 DOTA
[01:10]为家乡而战!完美世界城市挑战赛全国总决赛花絮
2019/07/25 DOTA
python进阶_浅谈面向对象进阶
2017/08/17 Python
Python+Selenium+PIL+Tesseract自动识别验证码进行一键登录
2017/09/20 Python
python中使用iterrows()对dataframe进行遍历的实例
2018/06/09 Python
PyQt5实现让QScrollArea支持鼠标拖动的操作方法
2019/06/19 Python
Puppeteer使用示例详解
2019/06/20 Python
django云端留言板实例详解
2019/07/22 Python
pandas read_excel()和to_excel()函数解析
2019/09/19 Python
Python 复平面绘图实例
2019/11/21 Python
对CSS3选择器的研究(详解)
2016/09/16 HTML / CSS
html5配合css3实现带提示文字的输入框(摆脱js)
2013/03/08 HTML / CSS
荷兰多品牌网上鞋店:Stoute Schoenen
2017/08/24 全球购物
纽约香氛品牌:NEST Fragrance
2018/10/15 全球购物
卫校中专生个人自我评价
2013/09/19 职场文书
农村产权制度改革实施方案
2014/03/21 职场文书
代理协议书范本
2014/04/22 职场文书
反腐倡廉观后感
2015/06/08 职场文书
oracle连接ODBC sqlserver数据源的详细步骤
2021/07/25 Oracle