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

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

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

python扩展实现方法--python与c混和编程(http://www.cnblogs.com/btchenguang/archive/2012/09/04/2670849.html)

混合编程:用 C 语言来扩展 Python 大法吧!(http://www.jianshu.com/p/09994c9d8489)

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

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

01

纯手写调用c语言

1、编写和调试C语言程序

在windows下编写c语言面临一个选择编译器的问题,不像linux一样可以直接选用gcc。这里我推荐使用VisualStudio2008作为c语言程序开发的IDE。如果你一开始就选择了vs2008,将在后期会省去很多工作。

这是因为python2.7在windows下的编译器就是使用vs2008的工具。当然如果你用别的版本的vs,后面也有解决方法。还有些同学选择使用gcc在windows下的版本,也就是minGccForWin。但是不推荐这种方法,据说这在后期会有无数莫名其妙的问题。

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; }

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

2、利用样板来包装代码

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

包含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)); }

最重要的两个个方法:

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

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

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

将c格式的结果按照指定格式转换成python格式。

下面是python和c对应的类型转换参数表:

参数转换.png

Py_BuildValue的用法表:

Py_BuildValue的用法表.png

注:上面两张图来自python扩展实现方法--python与c混和编程(http://www.cnblogs.com/btchenguang/archive/2012/09/04/2670849.html)

为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组

static PyMethodDefExtestMethods[] = {       
{"fac", Extest_fac, METH_VARARGS},    
  {"doppel", Extest_doppel, METH_VARARGS},       
{"reverse", Extest_reverse, METH_VARARGS},       
{NULL, NULL}
, };

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

增加模块初始化函数void initMethod()

void initExtest() {     Py_InitModule("Extest", ExtestMethods); }

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

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

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,一般都会碰到这个问题。以下给出解决方法:

1、先去下载Microsoft Visual C++ Compiler for Python 2.7(https://www.microsoft.com/en-us/download/details.aspx?id=44266)

2、 安装

再来试试。

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动态链接库。

调用的程序:

#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

另一种方法是直接install。即

python seup.py install

这样就可以直接import了。

4、性能测试

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

#coding=utf-8
import Extest as extes
timport timedef 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快的。至此,手写的方式介绍完毕。

02

使用Swig

使用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);         } }

03

配置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文件,大功告成。

04

使用

原文发布于微信公众号 - 人工智能LeadAI(atleadai)

原文发表时间:2017-10-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

linux实用技巧:你该使用ctags查找源码了

linux实用技巧:你该使用ctags查找源码了 ---- 1.ctags简介: “哦,这个多的文件,我该如何去查看XX函数的实现!”相信...

2636
来自专栏落影的专栏

研读《程序员的自我修养—链接、装载与库》

前言 《编译与链接过程的思考》 《静态库与动态库的思考》 在写完上面两篇思考之后,仔细研读《程序员的自我修养—链接、装载与库》,对编译、链接、装载、静态库和...

3937
来自专栏后端技术探索

vim神奇高效功能--批量生成Sql实例

可以通过写代码,读取文件进行入库,无论用什么语言,这代码逻辑都很容易实现。唯一的问题是代码需要上线后执行,每次上线可是一个大工程,恐怕会被pm同学嘲笑说:“你这...

833
来自专栏落影的专栏

为何百兆静态库能打进数兆的可执行文件?

前言 第三方库是工程开发必不可少的部分,而第三方库可以是.a和.framework的静态库,也可以是.framework的动态库,其中静态库是最常用的方式。 ...

4368
来自专栏北京马哥教育

初学python的30个操作难点汇总(新手必看篇)

初学Python的人总会遇到这样或者那样的问题,在我学习Python的这段时间我总结了自己的29个问题,具体如下: 1 在cmd下 盘与盘之间的切换 直接 D...

3207
来自专栏架构说

c++在编译中遇到符合不存在如何解决?

今日问题:symbol 不存在 : symbol lookup error: ./libinterface.so: undefined symbol: _ZN...

32815
来自专栏企鹅号快讯

8行代码实现ui文件到py文件转换

在用PyQt进行GUI编程时,一般先通过Qt Designer产生后缀为.ui的UI文件(类似于XML文件),接着将.ui文件转换成.py文件,再通过一个pyt...

2658
来自专栏北京马哥教育

15分钟学会shell脚本,最简明的教程在这里

本文由马哥教育M23期学员推荐,转载自互联网,作者为Hnongy,感谢作者的辛苦付出和贡献。 Shell脚本,就是利用Shell的命令解释的功能,对一个纯文本的...

2707
来自专栏玩转JavaEE

ElementUI中tree控件踩坑记

vhr部门管理模块更新啦!为了让小伙伴们快速理解部门管理模块实现思路,我想通过3篇短文来给大家介绍下大致的实现思路和核心代码。 项目地址:https://git...

3916
来自专栏决胜机器学习

PHP开发过程的那些坑(四) ——PDO bindParam函数

PHP开发过程的那些坑(四)——PDO bindParam函数 (原创内容,转载请注明来源,谢谢) 坑: bindParam是PDOStatement的一个方法...

2886

扫描关注云+社区