用Python使用C語言程序(Windows平臺(tái))

在機(jī)器學(xué)習(xí)中,很多時(shí)候我們需要Python和C的混合編程,最重要的原因是為了性能效率的提升: 解釋型語言一般比編譯型語言慢,一般提高性能的有效做法是,先做性能測試,找出性能瓶頸部分,然后把瓶頸部分在擴(kuò)展中實(shí)現(xiàn)。

本文的目標(biāo)是在windows平臺(tái)下(使用pycharm),實(shí)現(xiàn)python調(diào)用C語言編寫的程序。主要參考資料:

上面兩篇博客已經(jīng)寫得很詳細(xì),但是都是基于linux平臺(tái)和mac,我這里算是作為一篇windows平臺(tái)的補(bǔ)充和總結(jié),還有自己踩的一些坑,跟大家分享。

要使用python使用c語言編寫的程序,大致分成兩種方法,一種是純手寫,一種是用第三方的接口工具。本文將分成兩部分分別講述。

一.純手寫調(diào)用c語言

1.編寫和調(diào)試c語言程序

<p>
在windows下編寫c語言面臨一個(gè)選擇編譯器的問題,不像linux一樣可以直接選用gcc。這里我推薦使用VisualStudio2008作為c語言程序開發(fā)的IDE。如果你一開始就選擇了vs2008,將在后期會(huì)省去很多工作。這是因?yàn)閜ython2.7在windows下的編譯器就是使用vs2008的工具。當(dāng)然如果你用別的版本的vs,后面也有解決方法。還有些同學(xué)選擇使用gcc在windows下的版本,也就是minGccForWin。但是不推薦這種方法,據(jù)說這在后期會(huì)有無數(shù)莫名其妙的問題。
<p>ok,假設(shè)你安裝了vs的任何一個(gè)版本,我們編寫以下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函數(shù)實(shí)現(xiàn)的是字符串翻轉(zhuǎn)的功能,加入main函數(shù)是為了單元測試。

2.利用樣板來包裝代碼

<p>第一步調(diào)試完程序以后,要進(jìn)行代碼包裝。

  • 包含python頭文件
#include "Python.h"
  • 為每一個(gè)函數(shù)增加一個(gè)型如PyObject* Module_func()的包裝函數(shù)
static PyObject *Extest_reverse(PyObject *self, PyObject *args) {    
  char *orignal;    
  //s表示需要傳遞進(jìn)來的參數(shù)類型為字符串,如果是,就賦值給original,如果不是,返回NULL;
  if (!(PyArg_ParseTuple(args, "s", &orignal))) {        
    //包裝函數(shù)返回NULL,就會(huì)在Python調(diào)用中產(chǎn)生一個(gè)TypeError的異常
    return NULL;    
  }    
//需要把c中計(jì)算的結(jié)果轉(zhuǎn)成python對象,s代表字符串對象類型。
return (PyObject *)Py_BuildValue("s", reverse(orignal));
}

<p>最重要的兩個(gè)個(gè)方法:

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

將python格式的參數(shù)按照指定格式解析,轉(zhuǎn)存。

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

將c格式的結(jié)果按照指定格式轉(zhuǎn)換成python格式。
<p>下面是python和c對應(yīng)的類型轉(zhuǎn)換參數(shù)表:

參數(shù)轉(zhuǎn)換.png

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

注:上面兩張圖來自python擴(kuò)展實(shí)現(xiàn)方法--python與c混和編程
<p>

  • 為每個(gè)模塊增加一個(gè)型如PyMethodDef ModuleMethods[]的數(shù)組
static PyMethodDefExtestMethods[] = {    
  {"fac", Extest_fac, METH_VARARGS},    
  {"doppel", Extest_doppel, METH_VARARGS},    
  {"reverse", Extest_reverse, METH_VARARGS},    
  {NULL, NULL},
};

<p>有了這個(gè)聲明,python就可以方便地找到方法了。METH_VARARGS代表參數(shù)以tuple的形式傳入。

  • 增加模塊初始化函數(shù)void initMethod()
void initExtest() {    
  Py_InitModule("Extest", ExtestMethods);
}

最后加入在模塊被python導(dǎo)入時(shí)進(jìn)行調(diào)用的代碼。

<p>至此,包裝代碼的工作結(jié)束。把上面的代碼按順序組裝即可。

3.編譯與測試

編寫setup.py

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

激動(dòng)人心的時(shí)刻到了,開始編譯,輸入:

python setup.py build

但是,報(bào)錯(cuò)了,這是什么?

error: Unable to find vcvarsall.bat

還是編譯器出了問題。如果你沒有安裝VS2008,一般都會(huì)碰到這個(gè)問題。以下給出解決方法:

python setup.py build

為什么還是報(bào)同樣的錯(cuò)誤??

  • 3.手動(dòng)改寫注冊表
    這里要考慮你的python是32位還是64位的。
    打開regedit。添加項(xiàng):

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

此項(xiàng)下新建字符串值: 名稱:productdir
數(shù)據(jù):vcvarsall.bat所在路徑
注意:路徑中不包含最后的反斜杠。
再來試試。

python setup.py build

好的,這次成功了。項(xiàng)目目錄中新增了一個(gè)build文件夾:

build.jpg

我們用的時(shí)候只需要Extest.pyd文件即可。其實(shí)本質(zhì)上就是.dll動(dòng)態(tài)鏈接庫。

<p>調(diào)用的程序:

#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文件會(huì)直接到當(dāng)前目錄,直接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花費(fèi):'
print time.time()-start
start = time.time()
for j in range(100000):    
  python_reverse('string hahahahahaha')
print u'使用python花費(fèi):'
print time.time()-start

結(jié)果:

測試結(jié)果.jpg

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

<p>至此,手寫的方式介紹完畢。

二.使用Swig

<p>使用swig相對簡單,但是當(dāng)你習(xí)慣了手寫以后,相信手寫也是很方便的。當(dāng)然,不管你使用swig還是手寫,用windows的話,上面安裝vc編譯器還有修改注冊表的步驟都是繞不過去的。

1.下載、安裝swig

官網(wǎng)下載。
參考官方文檔
安裝完別忘了添加環(huán)境變量。

2.編寫、調(diào)試c語言程序

  • example.h
/*File: example.h*/
int fact(int n);
  • example.c
/* File: example.c */
//計(jì)算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語言程序,以及方法。
在終端運(yùn)行:

swig -python example.i

如果編譯的是C++文件,需要加上-C++選項(xiàng):

swig -c++ -python example.i

運(yùn)行完這個(gè)命令后,在工作目錄里會(huì)出現(xiàn)example_wrap.c和example.py,但是現(xiàn)在這個(gè)模塊還不能直接調(diào)用,因?yàn)檫€缺少動(dòng)態(tài)鏈接庫。
需要編寫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

這時(shí)目錄里多了一個(gè).pyd文件,大功告成。

4.使用

使用.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容