1. 程式人生 > 其它 >用Python使用C語言程式(Windows平臺)

用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

使用