用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_USERSoftwareMicrosoftVisualStudio9.0SetupVC 64位: HKEY_CURRENT_USERSoftwareWow6432NodeMicrosoftVisualStudio9.0SetupVC
此项下新建字符串值: 名称: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
使用
- ios textView跟随键盘的移动
- Android:屏保软件的开发
- CoordinatorLayout
- 从零开始的Spring Security Oauth2(二)
- 简化Swagger使用的自制Starter:spring-boot-starter-swagger,欢迎使用和吐槽
- demo3同通讯录展示的方式分组排序
- Android手势研究(textview及listview对比验证)
- demo2动态加载显示商品详情页
- demo1 动态显示view或弹框 动态隐藏view或弹框
- ios 继承UITableViewController,更改tableview样式
- demo1 动态显示view或弹框 动态隐藏view或弹框
- 从零开始的Spring Security Oauth2(一)
- 细说Android事件传递
- swift基础_ set get方法 理解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode之Bigram分词
- TensorFlow2 开发指南 | 01 手写数字识别快速入门
- Spring 数据初始 H2 后进行数据查询提示 Schema not found 错误
- Discourse CentOS 8 全新安装手册
- H2 数据库插入时间的方法
- Spring 配置的 H2 控制台 frameOptions 导致无法访问
- Spring Boot 和 Hibernate 的 H2 数据库配置来进行启动测试
- Java时间处理-LocalDateTime简介
- iOS音视频接入 - TRTC iOS端真机日志导出
- jQuery根据填写的input的数值导出excel表格
- 小程序根据返回值英文渲染出对应的中文
- 小程序返回的时间戳转化成时间
- 小程序使用 组件库 vant-weapp详细教程
- 小程序数据渲染根据返回值计算百分比并且显示
- JS获取当前系统时间戳的方法