1. 程式人生 > >Python之美[從菜鳥到高手]--一步一步動手給Python寫擴充套件(愛之初體驗)

Python之美[從菜鳥到高手]--一步一步動手給Python寫擴充套件(愛之初體驗)

    一直對Python擴充套件很感興趣,剛好看到了Extending and Embedding the Python Interpreter文件,看的是最低版本(由於工作中用的是2.x, ̄□ ̄),官方文件

   我使用的IDE是Code::Blocks 12.11,首先需要配置一下環境(windows)。

   由於需要呼叫Python提供的C API,需要設定incldue路徑,和lib路徑。

   開啟Code::Blocks ->> Settings,

   我選擇的編譯器是GCC,最好看一下安裝路徑,選擇的是CodeBlocks安裝時的MinGW, 如果你以前安裝過Qt等,可能會有所變化。

下面的編譯器和聯結器都是MinGW/bin目錄下的。


開啟Search directories選項卡,找到Python安裝路徑下的include和libs目錄,設定如下:



還需要設定pythonlib檔案。


   環境配置好了,需要編碼了,File ->> New ->> Project,在Projects的型別中我們選擇Shared library(共享庫),next之後語言選擇C,Project title設為spam,下面都預設就可以了。

將下面程式碼覆蓋main.c(我從文件中摘取的),

#include <Python.h>
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}

static PyMethodDef SpamMethods[] = {
    {"system",  spam_system, METH_VARARGS,"Execute a shell command."},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initspam(void)
{
    (void) Py_InitModule("spam", SpamMethods);
}

 編譯一下,如果看到下面輸出,恭喜你,成功了。
-------------- Build: Debug in spam (compiler: GNU GCC Compiler)---------------

mingw32-gcc.exe -Wall  -g    -IC:\Python26\include  -c F:\Cython\spam\main.c -o obj\Debug\main.o
mingw32-g++.exe -shared -Wl,--output-def=bin\Debug\libspam.def -Wl,--out-implib=bin\Debug\libspam.a -Wl,--dll -LC:\Python26\libs  obj\Debug\main.o   -o bin\Debug\libspam.dll  C:\Python26\libs\python26.lib 
Creating library file: bin\Debug\libspam.a
Output size is 34.08 KB
Process terminated with status 0 (0 minutes, 0 seconds)
0 errors, 0 warnings (0 minutes, 0 seconds)
 
   這是你會在你的bin/debug(release)下發現下面幾個檔案。


     這個libspam.dll就是我們需要的動態庫,將它改名為spam.pyd(python中的pyd其實就是dll),複製到你的python路徑/libs目錄下。

Ok,大功告成,下面就可以使用pyhon呼叫了。


    到這裡,有點小興奮,原來擴充套件 這麼簡單啊。如果你覺得完全用C寫的模組不太靈活,還可以簡單的用Python包裝一下,其實python中的很多標準庫都是這麼幹的,比如socket模組。你開啟python安裝目錄下的DLLs會發現_socket.pyd,那麼在socket.py中肯定有類似下面的:

import _socket
from _socket import *

   到了這裡,我們就來看看上面的程式碼蘊含了怎樣的玄機。

#include <Python.h> 

   這個大家都懂的,需要注意一點的就是在Python.h中可能包含一些預處理指令會影響C的標準標頭檔案,所以最好先宣告python.h。而且Python.h包含了一些常用標頭檔案,如stdio.h,string.h,errno.h,stdlib.h,所以偷懶點,就可以不宣告其餘標頭檔案了。

    你會很好奇為什麼會有一個self引數,還記得大一學資料結構時的連結串列嗎,對連結串列操作的函式是不是第一個引數都是連結串列指標,這的self會被預設設定為NUllPyArg_ParseTuple函式會根據格式("s")檢查args引數型別,並轉換成C變數的值,很明顯那個"s"應該是Python的string型別。Py_BuildValue和PyArg_ParseTuple功能相反,它會將C的值轉換成Python的值,所以會將system的返回值sts轉換成Python中的int型別。這也就是為什麼上面例子呼叫echo語句成功返回0。

    PyMethodDef SpamMethods陣列定義了需要匯出到Python中的名字,函式指標,引數型別,描述資訊。注意第三個引數,標誌了函式的引數型別。
METH_VARARGS代表的就是我們寫Python時的*args,而METH_KEYWORDS就是Python中的**kwargs,所謂的字典字典變數。描述資訊就在Python中就是DocString了。

    最後就需要初始化該模組了,注意名字必須是initXXX,其中XXX就是我們所說的模組名。也就是說我們重新命名的pyd檔名,initXXX和Py_InitModule(XXX)
三者必須一致。