使用2015cp範本練習".py"呼叫".pyd"。
編譯出Python的動態連結庫 :
從https://github.com/coursemdetw/2015cp下載老師的範本練習。
C程式的部分分成pymod和pyfun。
這兩組底下都有3個Node,分別是編譯Python動態連結庫用的button、呼叫用的Python程式和一個C++編成的函式。
button裡的內容如下:
... # compile os.system("Z:/C/MinGW/bin/gcc.exe -c -g -IV:/IDE/Python33/include -MMD -MP -MF build/"+target_name+".o.d -o build/"+target_name+".o "+filename) # link 因為 .c 程式中起始為 PyInit_sum, 因此 os.system("Z:/C/MinGW/bin/gcc.exe -shared -o dist/"+module_name+".pyd build/"+target_name+".o V:/IDE/Python33/libs/libpython33.a") g.es("done")
在執行時顯然對應不到W:槽的MinGW和Python34,所以必須改一下它的指令。
... # compile os.system("gcc -c -g -IW:/Python34/include -MMD -MP -MF build/"+target_name+".o.d -o build/"+target_name+".o "+filename) # link 因為 .c 程式中起始為 PyInit_sum, 因此 os.system("gcc -shared -o dist/"+module_name+".pyd build/"+target_name+".o W:/Python34/libs/python34.lib") g.es("pymod done")
而另一邊的pyfun同理。
接著就能在\dist資料夾中編譯出sum.pyd和pyfun.pyd了。
使用mypy.py :
接著回到@edit programs/dist/mypy.py和mypy2.py的兩個Node,只要在.leo中存檔就能建立和編輯它們。
mypy.py中導入sum,並使用它裡面的函式sum()。
print(sum.sum(1, 30))
在sum.pyd中,函式sum()是sum2()在sum.pyd中被定義的外部名稱,由以下可知。
// 定義內部運算的函式內容 int sum2(int a, int b) { return a+b; } // sum 函式的 interface static PyObject* mod_sum(PyObject *self, PyObject *args) { int a; int b; int s; // ii 表示兩個輸入變數都是整數 if (!PyArg_ParseTuple(args,"ii",&a,&b)) return NULL; // 這裡的 sum2 則是內部的函式定義, 與外部呼叫模組或函式名稱沒有直接關係 s = sum2(a,b); // i 表示 s 為整數 return Py_BuildValue("i",s); } // 這裡的 sum 是外部模組的呼叫名稱, 而 mod_sum 則是內部的呼叫名稱 (即 interface function) // Mod_Methods 為函式 (方法) 結構定義 static struct PyMethodDef Mod_Methods[] = { {"sum", mod_sum, METH_VARARGS, "Description.."}, {NULL,NULL,0,NULL} };
sum2()函式回傳的是兩個輸入值(pyd內部名為a和b),所以mypy.py收到後會顯示在畫面上。
另一邊的mypy2.py是使用pyfun.pyd,不過有兩個檔案太麻煩了,乾脆讓mypy.py導入兩個pyd就好了。
這次是加入一段字串。
print(pyfun.pyfun("我的字串"))
在pyfun.pyd中,pyfun()的外部名稱和它同名,在它的函式中,建立了一個指標to_who,偵測使用動態連結庫的對象,並把輸入進來的字串傳回。
pyfun(PyObject *self,PyObject *args) { const char *to_who; if(!PyArg_ParseTuple(args,"s",&to_who)) return NULL; // pyfun 函式會將輸入字串變數傳回 return PyUnicode_FromString(to_who); }
執行後的結果:
不過這兩個pyd的結尾不太一樣。
sum.pyd
// 模組啟始, PyInit_ 後必須使用"名稱".pyd 中的模組名稱, 以便啟動 // 換言之, 若編譯連結後的動態模組名稱為 sum.pyd, 則此地的起始函式名稱必須為 PyInit_sum PyMODINIT_FUNC PyInit_sum(void) { // 建立模組的起始, 輸入為模組結構名稱之 address (void) PyModule_Create(&ModMethods); }
pyfun.pyd
// 此 pyd 必須命名為 pyfun.pyd, 因為必須與 PyInit_後的名稱相同 PyInit_pyfun(void) { PyObject *m; m = PyModule_Create(&moduledef); return m; }
意思似乎是一樣的,不過pyfun.pyd將PyModule_Create當作回傳值回傳了,不知道用意為何。
若要瞭解Python的函式用法,應該要查閱Python.h或是相關說明才能明白。
延伸應用 :
只使用既定的範本實在是太簡略了,所以試著在pyd中加一些自己寫的函式。
在sum中加入新的函式sum3,並讓它的傳回值設定為a和b相加後開平方根,再乘上10。
添加了下列指令:
//導入 #include "math.h" //宣告 int sum3(int a, int b); //副程式sum3 int sum3(int a, int b) { return sqrt(a+b)*10; } //定義輸入值和sum3的外部名稱score static PyObject* mod_score(PyObject *self, PyObject *args) { int a; int b; int t; if (!PyArg_ParseTuple(args,"ii",&a,&b)) return NULL; t = sum3(a,b); return Py_BuildValue("i",t); }
並在PyMethodDef Mod_Methods[]中再新增一串:
{"score", mod_score, METH_VARARGS, "Description.."},
接著在mypy.py中呼叫sum.score()就行了。
Leo的搜尋功能和復原功能真的不太實在。常常搜尋時就跳到其他Node去,搜尋目標也要每次都重新調整;復原直接還原到存檔前的樣子,所以用它來編譯程式真的滿吃力的。
這次使用的是Python,所以SciTE和只能寫C語言的NetBeans幫不上忙,因為無法呼叫Python.h。說不定設定環境係數可以奏效?
不然只靠Leo編輯,它的函式分類也不是很清楚,常常不知道Python.h中的函式原型用法是甚麼,不像NetBeans可以連進去查看stdio.h或是windows.h所引用的函式。
Comments
comments powered by Disqus