Python C Extension
PS. ~夜晚的星空~(StarNight) 原發表於 HiNet Xuite DATE: 06/16/2015 10:02:44 PM
原因
在拜讀完Mosky大大的分享後,
想說除寫 Cython 外,可不可以直接寫 C code 讓 Python 呼叫,畢竟還是慣C阿!
找到一些文章教如何寫 C extesions,例如:Python Extension Programming with C。
在 Python 2.7 的時候,這些教學是 work 的,但我的電腦預設是 Python3 就 GG 了!此時瞬間了解為什麼這麼多的 Python2 libraries 還未支援 Python3 XDD
話雖如此,我還是要在我的電腦上寫 C extensions 阿!所以再翻了一些 Python3 C extensions 的文章,做一下整理~
環境
Archlinux
Python 3.4
gcc
Code
https://github.com/starnight/python-c-extension 裡頭有我的階進式練習,最簡單的從不傳參數的 Hello World 開始,接著是傳入參數,與最後的回傳值。
我的規畫:
- test.py 是平常寫的 Python 程式,裡頭會要 import 一些 C extension 的 module。
- 這些 module 是用 libxxx.h、libxxx.c 組成的。
- 為了要讓 Python 可以 import 這些 module,就透過 bind.c 用 Python 的 wrapper interface 把 module 包裝起來。
- 實際編譯則是使用 setup.py 的設定與執行,去把 wrapper 好的 Python C extension module 生出來。
- 最後 test.py 就可以 import 這個 Python C extension module。
- C extension module: helloworld
- functions in the module:
hello()
libmypy.h
- 是 libmypy.c 的 Header file
- Include 了 Python 要給 C 用的 Header file
- 宣告了一個 hello function,這 function 就是之後要給 test.py 使用的 function
libmypy.c
- hello function
- 實做了 libmypy.h 宣告的 hello function
- 回傳了一個 unicode 字串
- 因為 Python 3 預設的字串是 unicode,所以以 unicode 字串回傳
bind.c
- Python 佈署 C extension module 的 script
- 有關 setup 可以參考 Writing the Setup Script
- 但在簡單化的前提下,可以參考 Describing extension modules 的說明
- 因為這 C extension 有 libmypy.c 和 bind.c 要 build,所以在 ext_modules 的 Extension 裡標注 bind.c 和 libmypy.c
Makefile
- 因為是開發使用,不是要裝在 global 系統上。所以在用 python setup 時,加上
build_ext --inplace
參數,讓編譯出來的程式放在原本的路徑下。
01-HeyMan: With passed arguments
除了 00-HelloWorld 提到的部份,還多了
libmypy.h、libmypy.c
- heyman function
- Arguments:
- PyObject *self: 不論是哪一種 module function 都一定會有的參數,指向 module object
- PyObject *args: 指向由 arguments 組成的 tuple object
- Return value:
- PyObject *: module function 要將結果回傳給 Python 的物件。如果 return NULL,就代表有 error
- 這function會接收從Python傳來的兩個參數:
- num (interger)
- name (character pointer)
- 因為從 Python 傳給 module function 的參數會被打包成 PyObject,所以要用PyArg_ParseTuple function 把 args 拆解出要用的參數
- args: 傳入要被拆解的打包過的參數 PyObject 的 pointer
- format: 參數 PyObject 拆解出來的參數依序是哪一種 variable type 字串。可以參考 Parsing arguments and building values
- 後面參數依序是要用 variable 來接拆解出的參數
- 成功回傳 True;失敗回傳 False
- 最後將傳入的整數與字串 format、打包後, return 回 Python
bind.c
- 新增 heyman function 的說明字串 heymanfunc_docs
- 在 helloworld_funcs 新增 heyman 的 Method Define,設定為有參數要傳入
METH_VARARGS
- 呼叫和傳入參數,並將 heyman 回傳的字串顯示到 console
- 觀察 helloworld module 的 help
02-Add: With variables return
除了01-HeyMan提到的部份,還多了
libmypy.h、libmypy.c
- add function
- Arguments:
- PyObject *self: 不論是哪一種 module function 都一定會有的參數,指向 module object
- PyObject *args: 指向由 arguments 組成的 tuple object
- Return value:
- PyObject *: module function 要將結果回傳給 Python 的物件。如果 return NULL,就代表有 error。正常預期會回傳由「加法的結果」和「加法的方程式字串」組成的 tuple
- 這function會接收從Python傳來的兩個參數:
- num1 (interger) 被加數
- num2 (interger) 加數
- 一樣是由 PyArg_ParseTuple function 將 Python 傳入的參數拆解,並存回 num1 和 num2
- 利用 sprintf function,配合被加數 num1、加數 num2 組出加法的方程式字串,並存回 eq character array
- 因為要把回傳給 Python 的 variable 打包成 PyObject,所以透過 Py_BuildValue function 處理這樣的動作
- format: 說明後面要回傳給 Python 的 variable 是哪一種 variable type 的字串。可以參考 Parsing arguments and building values
- 後面參數依序是要回傳的 variable
- 如果只有 1 個 variable 要回傳,則回傳該 variable type 的 PyObject pointer;若是 2 個以上的 variable 要回傳,則 variables 會先組成 tuple,再打包回傳
- 成功回傳打包好的 PyObject pointer;失敗回傳 NULL
bind.c
- 新增 add function 的說明字串 heymanfunc_docs
- 在 helloworld_funcs新增 add 的 Method Define,設定為有參數要傳入
METH_VARARGS
- 呼叫和傳入參數,並將 add 回傳的字串顯示到 console
- 觀察 helloworld module 的 help
Reference