用Python使用C语言程序(Windows平台)

在机器学习中,很多时候我们需要Python和C的混合编程,最重要的原因是为了性能效率的提升: 解释型语言一般比编译型语言慢,一般提高性能的有效做法是,先做性能测试,找出性能瓶颈部分,然后把瓶颈部分在扩展中实现。

本文的目标是在windows平台下(使用pycharm),实现python调用C语言编写的程序。主要参考资料:

上面两篇博客已经写得很详细,但是都是基于linux平台和mac,我这里算是作为一篇windows平台的补充和总结,还有自己踩的一些坑,跟大家分享。

要使用python使用c语言编写的程序,大致分成两种方法,一种是纯手写,一种是用第三方的接口工具。本文将分成两部分分别讲述。

一.纯手写调用c语言

1.编写和调试c语言程序

<p>
在windows下编写c语言面临一个选择编译器的问题,不像linux一样可以直接选用gcc。这里我推荐使用VisualStudio2008作为c语言程序开发的IDE。如果你一开始就选择了vs2008,将在后期会省去很多工作。这是因为python2.7在windows下的编译器就是使用vs2008的工具。当然如果你用别的版本的vs,后面也有解决方法。还有些同学选择使用gcc在windows下的版本,也就是minGccForWin。但是不推荐这种方法,据说这在后期会有无数莫名其妙的问题。
<p>ok,假设你安装了vs的任何一个版本,我们编写以下c语言程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Python.h"
#define BUFSIZE 10
char *reverse(char *s) {    
  register char t;
  char *p = s; 
  char *q = (s + (strlen(s) - 1));    
  while (p < q) {       
    t = *p;      
    *p++ = *q;       
    *q-- = t;    
  }    
  return s;
}
int main() {    
  char s[BUFSIZE];    
  strcpy(s, "abcdef");    
  printf("reversing 'abcdef', we get '%s'\n", reverse(s));    
  strcpy(s, "madam");    
  printf("reversing 'madam', we get '%s'\n", reverse(s));    return 0;
}

<p>其中reverse函数实现的是字符串翻转的功能,加入main函数是为了单元测试。

2.利用样板来包装代码

<p>第一步调试完程序以后,要进行代码包装。

  • 包含python头文件
#include "Python.h"
  • 为每一个函数增加一个型如PyObject* Module_func()的包装函数
static PyObject *Extest_reverse(PyObject *self, PyObject *args) {    
  char *orignal;    
  //s表示需要传递进来的参数类型为字符串,如果是,就赋值给original,如果不是,返回NULL;
  if (!(PyArg_ParseTuple(args, "s", &orignal))) {        
    //包装函数返回NULL,就会在Python调用中产生一个TypeError的异常
    return NULL;    
  }    
//需要把c中计算的结果转成python对象,s代表字符串对象类型。
return (PyObject *)Py_BuildValue("s", reverse(orignal));
}

<p>最重要的两个个方法:

1.PyArg_ParseTuple(args, "s", &orignal)

将python格式的参数按照指定格式解析,转存。

2.y_BuildValue("s", reverse(orignal))

将c格式的结果按照指定格式转换成python格式。
<p>下面是python和c对应的类型转换参数表:

参数转换.png

Py_BuildValue的用法表:
Py_BuildValue的用法表.png

注:上面两张图来自python扩展实现方法--python与c混和编程
<p>

  • 为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组
static PyMethodDefExtestMethods[] = {    
  {"fac", Extest_fac, METH_VARARGS},    
  {"doppel", Extest_doppel, METH_VARARGS},    
  {"reverse", Extest_reverse, METH_VARARGS},    
  {NULL, NULL},
};

<p>有了这个声明,python就可以方便地找到方法了。METH_VARARGS代表参数以tuple的形式传入。

  • 增加模块初始化函数void initMethod()
void initExtest() {    
  Py_InitModule("Extest", ExtestMethods);
}

最后加入在模块被python导入时进行调用的代码。

<p>至此,包装代码的工作结束。把上面的代码按顺序组装即可。

3.编译与测试

编写setup.py

from distutils.core import setup, Extension
MOD = 'Extest'
setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest.c'])])

激动人心的时刻到了,开始编译,输入:

python setup.py build

但是,报错了,这是什么?

error: Unable to find vcvarsall.bat

还是编译器出了问题。如果你没有安装VS2008,一般都会碰到这个问题。以下给出解决方法:

python setup.py build

为什么还是报同样的错误??

  • 3.手动改写注册表
    这里要考虑你的python是32位还是64位的。
    打开regedit。添加项:

32位:
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Setup\VC
64位:
HKEY_CURRENT_USER\Software\Wow6432Node\Microsoft\VisualStudio\9.0\Setup\VC

此项下新建字符串值: 名称:productdir
数据:vcvarsall.bat所在路径
注意:路径中不包含最后的反斜杠。
再来试试。

python setup.py build

好的,这次成功了。项目目录中新增了一个build文件夹:

build.jpg

我们用的时候只需要Extest.pyd文件即可。其实本质上就是.dll动态链接库。

<p>调用的程序:

#coding=utf-8
import os
import sys
sys.path.append(os.getcwd() +"/build/lib.win32-2.7/") 
import Extest as extes
print extest.reverse('hello')

或者像这样:

python setup.py build_ext --inplace

这样,pyd文件会直接到当前目录,直接import即可。这种方法比较推荐!

目录.jpg

<p>另一种方法是直接install。即

python seup.py install

这样就可以直接import了。

4.性能测试

编写性能测试的代码如下:

#coding=utf-8
import Extest as extest
import time
def python_reverse(string):    
  return string[::-1]
start = time.time()
for i in range(100000):    
  extest.reverse('string hahahahahaha')
print u'使用c花费:'
print time.time()-start
start = time.time()
for j in range(100000):    
  python_reverse('string hahahahahaha')
print u'使用python花费:'
print time.time()-start

结果:

测试结果.jpg

可以看到,用c还是比python快的。

<p>至此,手写的方式介绍完毕。

二.使用Swig

<p>使用swig相对简单,但是当你习惯了手写以后,相信手写也是很方便的。当然,不管你使用swig还是手写,用windows的话,上面安装vc编译器还有修改注册表的步骤都是绕不过去的。

1.下载、安装swig

官网下载。
参考官方文档
安装完别忘了添加环境变量。

2.编写、调试c语言程序

  • example.h
/*File: example.h*/
int fact(int n);
  • example.c
/* File: example.c */
//计算n!
#include "example.h"
int fact(int n) {  
   if (n < 0){ 
    /* This should probably return an error, but this is simpler */      
    return 0; 
  }  
  else  if (n == 0) {          
    return 1;                  
  }  
  else {          
    /* testing for overflow would be a good idea here */          
    return n * fact(n-1);        
  }
}

3.配置swig,编译

  • example.i
/* File: example.i */
%module example
%{
#define SWIG_FILE_WITH_INIT
#include "example.h"
%}
int fact(int n);

配置文件声明了模块名称,原c语言程序,以及方法。
在终端运行:

swig -python example.i

如果编译的是C++文件,需要加上-C++选项:

swig -c++ -python example.i

运行完这个命令后,在工作目录里会出现example_wrap.c和example.py,但是现在这个模块还不能直接调用,因为还缺少动态链接库。
需要编写setup.py如下:

"""    setup.py file for SWIG example"""
from distutils.core import setup, Extension
example_module = Extension('_example',
                               sources=['example_wrap.c', 'example.c'],                         )
setup(name = 'example',    version = '0.1',    author = "SWIG Docs",    description = """Simple swig example from docs""",    ext_modules = [example_module],    py_modules = ["example"],    )

在终端里输入:

python setup.py build_ext --inplace

这时目录里多了一个.pyd文件,大功告成。

4.使用

使用.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容