天天看點

使用C寫Python的子產品1. 概述2. 引入 Python.h 頭檔案3. 編寫包裝函數4. 處理從 Python 傳入的參數5. 實作邏輯功能6. 處理 C 中的傳回值7. 注冊函數8. 注冊子產品9. 編譯

python 可以非常友善地和 c 進行互相的調用。

一般,我們不會使用 c 去直接編寫一個 python 的子產品。通常的情景是,我們需要把 c 的相關子產品包裝一下,然後在 python 中可以直接調用它。或者是,把 python 邏輯中的某一效率要求很高的部分使用 c 來實作。整個過程大概是:

引入 python.h 頭檔案。

編寫包裝函數。

函數中處理從 python 傳入的參數。

實作功能邏輯。

處理 c 中的傳回值,包裝成 python 對象。

在一個 pymethoddef 結構體中注冊需要的函數。

在一個初始化方法中注冊子產品名。

把這個 c 源檔案編譯成連結庫。

<a></a>

這個檔案一般位于 python 的主目錄中。比如我的 ubuntu 10.04 下,它的位置在:

在最後編譯的時候指定目錄就可以了。

<a></a>

因為 python 用到的函數與普通的 c 函數,在輸入和輸出上,會有一些不同,是以,我們需要把普通的 c 做一些封來給 python 用。

從另一方面來說,在實作功能的過程中,我們可以先完全不考慮這東西是拿給 python 用的,隻專注于使用 c 把它寫好就可以了。最後,功能寫好,測試沒有問題之後,再做 python 封裝的工作。

包裝函數一般聲明成 static ,并且第一個參數是一個預設傳入的 python 對象,就是 python 中某個對象的屬性方法一樣,第二個參數才是我們調用時傳入的參數(實際上它是一個序列化後的字元串):

<a></a>

因為我們的相關函數,之後是在 python 環境中被調用的,那麼它顯然接受的就是從 python 環境下傳入的參數。這和 c 中你看到的函數是不同的,在 python 的世界中,一切都是對象。是以,包裝函數中首先要處理的問題就是解析從 python 占擷取的參數。

常用的函數有: pyarg_parsetuple

pyarg_parsetuple 的作用是解析我們從 python 中傳入的 args 這個字元串,然後以我們規定的格式将解析結果放入指定變量的記憶體位。

" i|i " 就表示要把傳入的東西解析成兩個整數,同樣,還有 s 表示字元串等。

<a></a>

這部分沒什麼特别的,隻需要在 c 中一樣調用函數就可以了,相關變量我們已經在上一步處理過了。

<a></a>

我們使用 c 完成了功能邏輯, c 中會産生一個傳回值,要将這個值傳回到我們之前調用函數的 python 環境中,當然還需要經過一些處理才行。

常用的函數是: py_buildvalue 。

這個函數的用法和上一步中的 pyarg_parsetuple 是一樣的,它們過程相反。py_buildvalue 把 c 中的值按給定的格式格式化成 python 需要的對象。這裡注意一下,對于w_add 這個函數,我們可是聲明了它的傳回類型為 pyobject// 的哦。

<a></a>

在上面的實作完成之後,就需要作導出的準備了。第一步,就是要在一個類型為 pymethoddef的結構體中注冊我們需要導出到 python 中的函數:

這個結構體成員有四個函數:

" add " 導出後在 pyhton 中可見的方法名。

w_add 實際映射到 c 中的方法名。

meth_varargs 表示傳入方法的是普通參數,當然還可以處理關鍵詞參數。

此方法的注釋。

<a></a>

在注冊了方法後,就要注冊此子產品了。方法是定義一個 init// 的函數:

方法名必須是 init 加上子產品名,然後調用 py_initmodule 來注冊子產品,這個函數的第一個參數就是子產品名,第二個參數是此子產品中我們導出的方法,就是上一步我們定義的結構體。

<a></a>

最後一步就是編譯了。沒什麼特别的,指定好 python.h 頭檔案的位置就可以了:

當然,連結庫的名字要和我們期望導出的子產品名一緻。

這樣,你就可以在 python 中使用 import 直接引入 demo 子產品,然後調用它的 add 方法了: