Scalers點評:在2015年,ScalersTalk成長會完成Python小組完成了《Python核心編程》第1輪的學習。到2016年,我們開始第二輪的學習,并且將重點放在章節(jié)的習題上。Python小組是成長會內部小組,如果你想和我們一起學習Python,你需要是成長會成員,并且完成相關進群任務。 我們的節(jié)奏是一周一章學到位,章節(jié)習題都會一個一個過。通過到位地執(zhí)行,達到實質性的提升。 第22周學習情況匯總本周學習情況本周(20160703-20160710)學習第22章,章節(jié)內容為《擴展python》。本周行動主持人為寶二爺。 往期日志: 本章大綱第22章介紹了如何編寫擴展代碼并將其功能整合到Python編程環(huán)境中。包括什么是擴展、為什么要擴展、如何創(chuàng)建以及一些相關話題。復盤重點為書中的一個實例。 知識點22.1 介紹/動機22.1.1 What擴展:所有能被整合或導入到其他Python腳本的代碼 特點:擴展和解釋器之間的交互方式與Python模塊一樣 22.1.2 Why好處: 添加/額外的(非Python)功能:Python核心不提供的功能,可以通過擴展實現 性能瓶頸的效率提升:解釋型語言運行速度比編譯型語言慢。把瓶頸部分在擴展中實現,可以提升整體性能。 保持專有源碼私密:腳本語言執(zhí)行源代碼,無法保密。編譯語言只要發(fā)布二進制文件即可,能實現保密。
22.2 創(chuàng)建Python擴展說明:以下程序在Ubuntu15.10,64bit系統(tǒng),Python 2.7.10版本下測試通過。 22.2.1 創(chuàng)建應用程序代碼fac :遞歸求階乘
reverse :字符串反轉算法
Example 22.1 code 見 test.c 使用下列指令 gcc test.c -o test
./test
輸出結果: 4! == 24 8! == 40320 12! == 479001600 reversing 'abcdef', we get 'fedcba' reversing 'madam', we get 'madam'
22.2.2 利用樣板包裝代碼1. 包含Python的頭文件#include "Python.h"
2. 為每個模塊的每一個函數增加一個型如PyObject* Module_func 的包裝函數為所有想被Python環(huán)境訪問到的函數都增加一個靜態(tài)函數,返回類型為PyObject * ,函數名格式為模塊名_函數名 ; 表22.1 Python和C/C++數據轉換函數 完整的函數: static PyObject * Extest_fac(PyObject *self, PyObject *args) { int res; // parse result int num; // arg for fac PyObject* retval; // return value res = PyArg_ParseTuple(args,"i", &num); if (!res) { // TypeError return ; } res = fac(num); retval = (PyObject*)Py_BuildValue("i",res); return retval; }
減少中間量,縮短代碼 static PyObject * Extest_fac(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args,"i", &num)) return ; return(PyObject*)Py_BuildValue("i", fac(num)); }
內存泄漏:內存被申請了,但沒有被釋放。 Py_BuildValue 函數生成要返回Python對象的時候,會把轉入的數據復制一份。上面的兩個字符串都被復制出來。但是我們申請了用于存放第二個字符串的內存,在退出的時候沒有釋放掉它。于是內存就泄露了。
正確的做法是:先生成返回的python對象,然后釋放在包裝函數中申請的內存。 static PyObject * Extest_doppel(PyObject *self, PyObject *args) { char *orig_str; //原始字符串 char *dupe_str; //反轉后的字符串 PyObject* retval; if (!PyArg_ParseTuple(args,"s", &orig_str)) return ; retval =(PyObject*)Py_BuildValue("ss", orig_str, \ dupe_str =reverse(_strdup(orig_str))); free(dupe_str); return retval; }
3. 為每個模塊增加一個型如PyMethodDef ModuleMethods 的數組完成包裝函數后,列在某個地方,以便python解釋器能夠導入并調用它們。這個就是ModuleMethods 數組要做的事。 static PyMethodDef ExtestMethods = { { "fac", Extest_fac,METH_VARARGS }, { "doppel", Extest_doppel,METH_VARARGS }, { , }, };
4. 增加模塊初始化函數void initMethod void initExtest { Py_InitModule("Extest",ExtestMethods); }
22.2.3 編譯與測試1. 創(chuàng)建setup.py 編譯最主要的內容由setup 函數完成,要為每一個擴展創(chuàng)建一個Extension實例,本例只有一個擴展,只需創(chuàng)建一個實例。 Extension('Extest', sources=['Extest.c']) ,第一個參數是擴展的名字,如果模塊是包的一部分,還需要加”.”;第二個參數是源代碼文件列表
setup('Extest', ext_modules=[...]) ,第一個參數表示要編譯哪個東西,第二個參數列出要編譯的Extension對象。
#!/usr/bin/env python from distutils.core import setup, Extension MOD = 'Extest' setup(name=MOD, ext_modules=[Extension(MOD,sources=['Extest.c'])])
2. 運行setup.py 編譯鏈接代碼輸入命令:python setup.py build 3. Python中導入模塊輸入命令:sudo python setup.py install 說明:如果不加sudo ,出現如下拒絕訪問的錯誤: error: [Errno 13] Permission denied: '/usr/local/python2.7/lib/python2.7/site-packages/Extest-0.0.0-py2.7.egg-info
加上sudo 后輸入密碼即可獲得權限。 4. 測試測試函數:把main 改名為test ,加上Extest_test 包裝起來。 static PyObject * Extest_test(PyObject *self, PyObject *args) { test; return (PyObject*)Py_BuildValue(""); }
在ExtestMethods 加入這個函數,即增加一行代碼:{"test", Extest_test, METH_VARARGS }, 調用結果如下: 完整的代碼見Extest.c 22.2.3 線程和全局解釋鎖可以使用兩個C宏Py_BEGIN_ALLOW_THREADS 和Py_END_ALLOW_THREADS 保證運行和非運行時的安全性。由這些宏包裹的代碼允許其他線程的運行。 22.2.4 引用計數表22.3 用于Python對象引用計數的宏 上述函數檢查對象是否為空的版本,Py_XINCREF 和Py_XDECREF 22.3 相關話題SWIG:根據特別注釋過的C/C++頭文件生成包裝代碼,可以省去樣板代碼的時間 Pyrex:具有C數據類型的Python Psyco:just-in-time(JIT)編譯器,優(yōu)化代碼速度 嵌入:把Python解釋器包裝到C程序中。 練習22-1. 擴展Python. 編寫Python擴展都有些什么好處? 添加/額外的(非Python)功能 性能瓶頸的效率提升 保持專有源碼私密
22-2. 擴展Python. 編寫Python擴展都有些什么不好的地方或是危險的地方? 內存泄漏 測試函數代替main 引用計數 多線程環(huán)境 要編寫C/C++代碼(TAT )
22-3. 編寫擴展。下載或找到一個C/C++編譯器,并寫一個小程序(重新)熟悉一下C/C++編程。找到你的Python所在的目錄,并找到Misc/Makefile.pre.in文件。把你剛寫的程序包裝到Python當中。按步驟把你的模塊編譯成動態(tài)庫,從Python中調用你的模塊并測試一下是否正確。 22-4. 把Python移植到C。選幾個你在前幾章寫的代碼,并把它們作為模塊移植到C/C++中。 22-5. 包裝C代碼。找一段你之前寫的,想移植到Python的C/C++代碼。不要去移植,把這段代碼改成擴展模塊。 22-6. 編寫擴展。在13-3的練習中,你寫了一個dollarize函數,它能把浮點型轉為前置美元符號,逗號分隔的貨幣金額字符串。請創(chuàng)建一個擴展,包裝dollarize函數,并在模塊中增加一個回歸測試函數test。附加題:除了創(chuàng)建C擴展外,再用Pyrex重寫dollarize函數。 22-7. 擴展和嵌入。擴展和嵌入的區(qū)別是什么? 擴展:把C代碼包裝到Python中 嵌入:把Python解釋器包裝到C的程序中
ScalersTalkID:scalerstalk 本微信公眾號作者Scalers,游走在口譯世界的IT從業(yè)者。微信公眾號ScalersTalk,微博@Scalers,網站ScalersTalk.com,口譯100小時訓練計劃群C 456036104 成長會是由Scalers發(fā)起的面向成長、實踐行動,且凝聚了來自全球各地各行各業(yè)從業(yè)者的社群。有意入會者請和Scalers直接聯(lián)系,我和其他會員會和你直接交流關于成長行動等各方面的經驗教訓。2016年成長會持續(xù)招募中,參見做能說會寫的持續(xù)行動者:ScalersTalk成長會2016年會員計劃介紹(2016.3更新)
|