擴充套件Python之在Python中呼叫C編寫的函式模組
阿新 • • 發佈:2018-12-20
目錄
- 編寫Python擴充套件
編寫Python擴充套件
1. 建立應用程式碼
-
編寫一個比較簡單的函式,求階乘。並且寫上main函式進行測試。
-
編譯測試
$ gcc Extest1.c -o Extest
$ ./Extest
4! = 24
2. 根據樣板編寫封裝程式碼
- 先給個文件地址:Extending Python with C or C++
2.1 包含Python標頭檔案
- 在大多數類UNIX系統上,Python包含檔案一般位於
/usr/local/include/python2.x
或者/usr/include/python2.x
中。 - 我的系統是ubuntu 18.04 LTS。沒有python2,系統自動安裝的只有python3。我的位於
/usr/include/python3.6
中
- 將
Python.h
#include "Python.h"
- 這邊之後編譯這個檔案時可能會出現錯誤
fatal error: Python.h: No such file or directory compilation terminated.
。
stack overflow上的解決方案
sudo apt-get install python3-dev
2.2 為每一個模組函式新增形如PyObject* Module_func() 的封裝函式
- 對於每個需要在Python環境中訪問的函式,需要建立一個以
static PyObjuec*
標識,以模組名開頭,緊接著是下劃線和函式名本身的函式。 - 例如,若要讓
fac()
函式可以在Python中匯入,並將Extest
作為最終的模組名稱,需要建立一個名為Extest_fac
的封裝函式。在用到這個函式的Python指令碼中,可以使用import Extest
和Extest.fac()
的形式在任意地方呼叫fac()
函式。 - 封裝函式的任務是將Python中的值轉成C形式,接著呼叫相應的函式。當C函式執行完畢時,需要返回Python的環境中。封裝函式需要將返回值轉換成Python形式,並進行真正的返回,傳回所有需要的值。
2.3 為每一個模組函式新增一個PyMethodDef ModuleMethods[] 陣列/表
- 這邊書裡《Python核心程式設計》和文件稍微有點出入,書裡是3個引數,文件是4個引數,但是文件沒有寫第4個引數是什麼,所以這邊先按書裡寫的。
2.4 新增模組初始化函式
- 書裡是這麼寫的,我懷疑是python2和3的區別,所以這裡書裡的不太對,我這邊在後面的執行中會出現問題,所以這裡稍微改一下。用文件的內容。
- 文件裡的初始化格式:
3 編譯並測試
3.1 建立 setup.py
3.2 執行 setup.py 來編譯並連結程式碼
- 執行該命令構建擴充套件
python3 setup.py build
- Error 1: 找不到python.h。解決方案見上
- Error 2: 用書裡的函式會顯示沒有
initModule
這個函式。解決方案見上 - Error 3: 顯示找不到
distutils
模組No module named 'distutils.core'
。解決方案
sudo apt-get install python3-distutils
3.3 在Python中匯入模組
- 擴充套件模組會建立在build/lib.*目錄下,即執行setup.py指令碼的位置。要麼切換到這麼目錄中,要麼用下面的方式將氣安裝到Python中。
$ python3 setup.py install
- Error 這裡有了問題,注意最後一行
Permission denied
,所以這裡用sudo python3 setup.py install
就可以了。
3.4 測試函式
4. 文件擴充套件
4.1 在2.2中出現的 int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
函式
- 函式文件 1.7
- 在
fac()
的示例中,當客戶程式呼叫Extest.fac(
)時,會呼叫封裝函式。這裡會接受一個Python整數,將其轉換成C整數,接著呼叫C函式fac(),獲取返回結果,同樣是一個整數。將這個返回值轉換成Python整數,返回給呼叫者(記住,編寫的封裝函式就是def fac(n)宣告的代理函式。當這個封裝函式返回時,就相當於Python fac()函式執行完畢了)。 - 如何完成轉換?Python -> C :
PyArg_Parse*()
. C->Python :Py_BuildValue()
PyArg_Parse*()
: 類似於C中的sscanf
函式。接受一個位元組流,然後根據一些格式字串進行解析,將結果放入到相應指標所指的變數中。若解析成功就返回1;否則返回0.Py_BuildValue()
:類似於sprintf
函式,接受一個格式字串,並將所有引數按照格式字串指定的格式轉換成一個Python 物件。
Python和C/C++之間的“轉換編碼”
格式編碼 | Python資料型別 | C/C++資料型別 |
---|---|---|
s, s# | str/unicode, len() | char*, int |
i | int | int |
b | int | char |
c | str | char |
d | float | double |
h | int | short |
l | int | long |
取自文件的例子:文件1.7部分
#include "Python.h"
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
ok = PyArg_ParseTuple(args, ""); /* No arguments */
/* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
/* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
/* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
/* A pair of ints and a string, whose size is also returned */
/* Possible Python call: f((1, 2), 'three') */
{
const char *file;
const char *mode = "r";
int bufsize = 0;
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
/* A string, and optionally another string and an integer */
/* Possible Python calls:
f('spam')
f('spam', 'w')
f('spam', 'wb', 100000) */
}
{
int left, top, right, bottom, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&left, &top, &right, &bottom, &h, &v);
/* A rectangle and a point */
/* Possible Python call:
f(((0, 0), (400, 300)), (10, 10)) */
}
4.2 在3.1中建立的setup.py
。
- 官方文件在這裡Writing the Setup Script
參考書籍:
《Python核心程式設計》Unit8
百度網盤連結:https://pan.baidu.com/s/1XYSY65_BuQkEHm-LFzzi3g 提取碼:awqc
《Python3文件》https://docs.python.org/3/ 這篇文章主要需要用到的文件內容上面已經給了