摘要:哪怕工作中比較少機會自己寫擴展了解這塊的知識,也有利于我們更加深入了解的運行本質。表示這個模塊使用這個映射表。所以,在我們編寫擴展時,也需要時刻謹記這步主要會用到下面兩個宏增加引用例減少引用例不能直接使用釋放,必須使用然后即可。
前言
使用 Python 毋庸置疑減少了很多規則約束和開發成本,讓我們能夠更加專注于邏輯而非語法。但是得此失彼,開發效率提高了,卻帶來了運行性能的問題,所以就常常被其他門派追著暴打。
身為一個 pythoner,我們也很憂傷呀,怪我們咯..
萬幸的是,雖然上帝關掉了我們一扇門,但是卻為我們打開了另一扇窗,正因為底層是用 C語言 寫的,所以我們可以將一些性能損耗比較大的功能,或者模塊,通過 C語言 重寫,然后 import xxxx 來無縫結合。
哪怕工作中比較少機會自己寫C擴展, 了解這塊的知識,也有利于我們更加深入了解 Python 的運行本質。
網上比較是通過 ctypes 或者 setup.py 的方式實現引用和編譯安裝,這邊想試下最原始的方法~
快速開車 1. 實現接口函數接口函數是什么意思?可以簡單理解成就是 Python 和 C 的對接函數,舉個栗子:
static PyObject *test(PyObject *self, PyObject *args){ int arg1, arg2; if(!(PyArg_ParseTuple(args, "ii", &arg1, &arg2))){ return NULL; } return Py_BuildValue("i", arg1 + arg2 * 10); }
我們可以看到這個函數和傳統意義上的 C 用法用點不同了,特別是在函數形參那邊的PyObject self, PyObject args
第一個參數是 PyObject *self,這個參數是Python內部使用的,可以不用管;
第二個參數是 PyObject *args,這個參數非常重要,因為這個攬括了所有傳給函數的參數。它是一個參數列表,把所有的參數都整合到
一個 string, 因此,如果我們需要解析這些參數需要用特定的姿勢!我們需要用到 PyArg_ParseTuple 來解開這個扣人心弦的入口!
PyArg_ParseTuple 函數說明:
args就是需要轉換的參數;
ii 就是參數類型的格式符號,這里代表 int init;(更多類型請看官網:https://docs.python.org/2/c-a...)
后面的 &arg1, &arg2 就是通過參數解析提取的值,存放的地方,這有點類似 C 的 scanf;
很明顯,這三個參數,在數量上存在這一定的聯系,也就是,傳進去兩個 int參數,那么就肯定是對應了兩個 ii,然后就會對應存在 兩個實際的"容器"內,這里要注意,一不小心就會 Segmentation fault
對應有解析參數的,肯定也有 C模塊 值轉換成 Python對象 的,那就是 Py_BuildValue。
Py_BuildValue 函數說明:
# 對比著來看 PyArg_ParseTuple(args, "ii", &arg1, &arg2) Python -> C模塊 Py_BuildValue("i", arg1 + arg2 * 10); C 模塊 -> Python
第一個參數 和 PyArg_ParseTuple 的第二個參數一樣,都是格式化符號;
第二個參數是需要轉換的參數,函數 Py_BuildValue 會把所有的返回指都組裝成 tuple 給 Python
相關的官方文檔:https://docs.python.org/2/c-a...
2. 定義方法列表# 示例 static PyMethodDef testMethods[] = { {"test", test, METH_VARARGS, "This is test"}, {NULL, NULL, 0, NULL} };
PyMethodDef 是一個 C結構體,用來完成一個映射,也就是便于方法查找,我們把需要被外面調用的方法都記錄在這表內。
PyMethodDef 結構體成員說明:
第一個字段:在 Python 里面使用的方法名;
第二個字段:C 模塊內的函數名;
第三個字段:方法參數類型,是無參數(METH_NOARGS) , 還是有位置參數(METH_VARARGS), 還是其他等等;
第四個字段:方法描述,就是通過 help() 或者 doc 可以看到的;
需要注意的是,這個列表的最后必須以 {NULL, NULL, 0, NULL} 的形式來代表聲明結束,也有一些大佬用 {NULL, NULL},不過個人覺得寫完整也不會累到哪去, 相反會比較直觀。
# PyMethodDef 結構體定義源碼, 取自 Python2.7 Include/methodobject.h struct PyMethodDef { const char *ml_name; /* The name of the built-in function/method */ PyCFunction ml_meth; /* The C function that implements it */ int ml_flags; /* Combination of METH_xxx flags, which mostly describe the args expected by the C func */ const char *ml_doc; /* The __doc__ attribute, or NULL */ };
正因為存在這樣的一份記錄表,Python 才能夠尋找到相應的函數
同樣的,如果我們想要找一個模塊的 Python 函數 對應什么的 C模塊方法,也能通過這地方比較粗暴得知,例如 Python 的 list
# 取自 Python2.7 object/listobject.c static PyMethodDef list_methods[] = { {"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, getitem_doc}, {"__reversed__",(PyCFunction)list_reversed, METH_NOARGS, reversed_doc}, {"__sizeof__", (PyCFunction)list_sizeof, METH_NOARGS, sizeof_doc}, {"append", (PyCFunction)listappend, METH_O, append_doc}, {"insert", (PyCFunction)listinsert, METH_VARARGS, insert_doc}, {"extend", (PyCFunction)listextend, METH_O, extend_doc}, {"pop", (PyCFunction)listpop, METH_VARARGS, pop_doc}, {"remove", (PyCFunction)listremove, METH_O, remove_doc}, {"index", (PyCFunction)listindex, METH_VARARGS, index_doc}, {"count", (PyCFunction)listcount, METH_O, count_doc}, {"reverse", (PyCFunction)listreverse, METH_NOARGS, reverse_doc}, {"sort", (PyCFunction)listsort, METH_VARARGS | METH_KEYWORDS, sort_doc}, {NULL, NULL} /* sentinel */ };3. 實現初始化函數 (關鍵)
PyMODINIT_FUNC inittest(){ Py_InitModule("test", testMethods); }
需要特別注意的是,這個函數名不能像上面那樣,這是有規定的,必須是 init + 模塊名字,比方說,我的最后編譯出來的文件是 test.so, 那我的函數名就是 inittest, 這樣在 Python 導入 test 模塊時,才能找到這個函數并調用。
這里調用了 Py_InitModule 函數來將模塊名字和映射表結合在一起。表示 test 這個模塊使用 testMethods 這個映射表。
4. 注意對象引用管理和內存泄露Python 在對象管理、內存管理上面,有引用計數,標記-清除,分代回收等策略,其中引用計數發生的頻率會非常非常高,依賴這個通常已經能夠解決大部分的對象殘留問題了。
所以,在我們編寫 C擴展 時,也需要時刻謹記這步.
主要會用到下面兩個宏:
1. 增加引用: Py_INCREF 例: Py_INCREF(pObj1) 2. 減少引用: Py_DECREF 例: Py_DECREF(pObj1)
不能直接使用 free/delete 釋放,必須使用 Py_DECREF(pObj1), 然后 pObj1 = NULL 即可。
具體可以參考:
官網:https://docs.python.org/2/ext...
垃圾回收機制: http://www.wklken.me/posts/20...
gcc -I /usr/include/python2.7/ -fpic --shared -o test.so test.c完整例子
test.c
#includestatic PyObject *test(PyObject *self, PyObject *args){ int arg1, arg2; if(!(PyArg_ParseTuple(args, "ii", &arg1, &arg2))){ return NULL; } return Py_BuildValue("i", arg1 + arg2 * 10); } static PyMethodDef testMethods[] = { {"test", test, METH_VARARGS, "This is test"}, {NULL, NULL} }; PyMODINIT_FUNC inittest(){ Py_InitModule("test", testMethods); }
test.py
import test print test.test(1, 2) # 輸出 21
歡迎各位大神指點交流, QQ討論群: 258498217
轉載請注明來源: https://segmentfault.com/a/11...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42055.html
摘要:初體驗從零開始重構計算模塊從屬于筆者的前端入門與工程實踐,更多相關資料文章參考學習與實踐資料索引和學習與實踐資料索引。不過筆者也只是了解其概念而未真正付諸實踐,本文即是筆者在將我司某個簡單項目中的計算模塊重構為過程中的總結。 WebAssembly 初體驗:從零開始重構計算模塊從屬于筆者的 Web 前端入門與工程實踐,更多相關資料文章參考WebAssembly 學習與實踐資料索引和 ...
摘要:廣告歡迎大家到路飛學城學習很喜歡薪時代這個詞所以我們要擁抱人工智能擁抱前提下載,如果是電腦會自帶。 廣告:歡迎大家到 路飛學城 學習 Python~ 很喜歡 Python 薪時代 這個詞~所以我們要擁抱 人工智能~擁抱 Python~ 前提:下載 Python,如果是 Mac 電腦會自帶 Python。 Hello World! Mac 電腦打開終端輸入: cd desktop tou...
摘要:靜態資源路徑可以有多個,所以這里使用一個列表進行配置再次進入,完美后記現在只涉及到了項目的配置和一些基礎的配置,沒有涉及到請求從開始到完成的任何內容。下篇教程將集中進行記錄。 前言 推薦使用 virtualenv 創建 python 虛擬環境,防止因為使用 pip 安裝依賴到全局引起版本沖突的問題,PyCharm 默認會生成一個 venv 目錄并創建虛擬環境,使用 IDE 自帶的終端...
閱讀 3201·2021-11-18 10:02
閱讀 1456·2021-10-12 10:08
閱讀 1255·2021-10-11 10:58
閱讀 1277·2021-10-11 10:57
閱讀 1173·2021-10-08 10:04
閱讀 2125·2021-09-29 09:35
閱讀 778·2021-09-22 15:44
閱讀 1279·2021-09-03 10:30