本系列教程供個人學習筆記使用,如果您要瀏覽可能需要其它編程語言基礎(如C語言),why?因為我寫得爛啊,只有我自己看得懂!!
python語言可以對c程序代碼進行調用,以彌補python語言低性能的缺點。當然,它也不是直接就可以調用,需要我們對c代碼進行一些中間過程處理,其基本流程如下:
1.創建c程序功能代碼
------------1.1創建.c源程序文件(py_test1.c)
------------1.2創建.h頭文件(py_test1.h)
2.python類型適配,包裝c代碼(寫包裹文件)(py_test1wrapper.c)
------------2.1.包含Python.h頭文件(在python安裝目錄下的include目錄下找到)
------------2.2.為每一個函數設置一個PyObject *Module_func()的包裹函數
------------2.3.為模塊增加一個PyMethodDef ModuleMethods[]的數組
------------2.4.增加模塊的初始化函數void initModule()
3.編譯和測試
-------------3.1編譯安裝到python環境
--------------------3.1.1)創建setup.py
--------------------3.1.2)運行setup.py編譯和鏈接c的擴展代碼
-------------3.2測試
--------------------3.2.1)從Python中導入模塊
--------------------3.2.2)測試
- 創建c程序功能代碼
一、創建.c源程序文件py_test1.c
這是程序的具體功能代碼,也就是python需要調用的c源程序。這里主要寫了三個方法,最終我們就會實現python來調用這三個方法。1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 6 //求階乘 7 int fac(int n) { 8 if(n < 2) 9 return 1; 10 11 return n*fac(n-1); 12 } 13 14 15 16 //字符串逆序 17 char *reverse(char *s) { 18 //比如輸入abcdefg,則返回gfedcba 19 char t,*p = s ,*q = (s+strlen(s)-1); 20 21 while(s && (p<q)) { 22 t = *p; 23 *p++ = *q; 24 *q-- = t; 25 } 26 27 return s; 28 } 29 30 31 int test(void) //測試main方法,改成普通的test方法 32 // int main(void) 33 { 34 char s[1024]; 35 36 printf("5! = %d\n",fac(5)); //5的階乘 37 38 printf("10! = %d\n",fac(10)); // 10的階乘 39 40 41 strcpy(s,"hello world"); 42 printf("reversing 'hello world',we get '%s'\n",reverse(s)); 43 44 return 0; 45 }
二、創建.h頭文件py_test1.h
接下來寫一個就像stdio.h這樣的頭文件,方面后面引用。這個文件里面主要就是聲明了py_test1.c中的三個方法。1 #ifndef PYTEST1_H_ 2 #define PYTEST1_H_ 3 4 int fac (int n) ; 5 char *reverse(char *s) ; 6 int test(void) ; 7 8 #endif
- 寫包裹文件py_test1wrapper.c
這個包裹文件其實也是一段c語言代碼,只是這一段代碼比較特殊,它需要除c語言外還有一定的規則來創建它,這些規則就是python調用c定義的,必須遵循。
1.必須include包含Python.h這個頭文件,可以說這里就慢慢地去靠向python了。這個頭文件在python安裝目錄下的include目錄下找到它,但我們並不需要去知道它在哪兒。
2.必須include包含py_test1.h這個頭文件,這個就是上一步我們創建的那個頭文件。
3.還記得我們的py_test1.c這個源程序文件嗎?現在我們需要對它里面的每個方法(這里是三個)都要設置一個包裹函數,它必須以PyObject為返回值,並且每個函數都有兩個必須的參數。
4.還要在這個文件里添加一個模塊數組,類型必須是PyMethodDef,它用來定義方法名以及方法名與包裹函數的對應關系。
5.最后還要添加一個模塊的初始化函數void initModule(),這里的函數名必須以init和模塊名組成。
下面就是這個包裹文件的完整代碼:1 #include "Python.h" 2 #include <stdlib.h> 3 #include <string.h> 4 #include "py_test1.h" 5 /** 6 7 **包裹文件 8 9 **/ 10 11 //為fac函數設置包裹函數(函數名、參數都有一定的規則,要注意) 12 static PyObject *py_test1_fac(PyObject *self,PyObject *args) 13 { 14 15 int num ; 16 //將python的數據類型int args通過i的方式轉換成能被c識別的類型int num 17 //i:表示將python的整型轉成c的整型 ,其它類型可百度 18 if (!PyArg_ParseTuple(args,"i",&num)) 19 return NULL; 20 21 22 //調用c的對應函數並得到返回值, 23 //然后將返回值c的數據類型int通過i的方式轉換成能被python識別的類型int 24 //最后強轉成PyObject類型 25 return (PyObject *)Py_BuildValue("i",fac(num)); 26 27 } 28 29 30 //為reverse函數設置包裹函數(由於python中有reverse函數,不能使用) 31 static PyObject *py_test1_doppel(PyObject *self,PyObject *args) 32 { 33 char *src; 34 char *mstr; 35 PyObject *retval; 36 37 //s:python中str ----->C中char * 38 if (!PyArg_ParseTuple(args,"s",&src)) 39 return NULL; 40 41 //申請存儲空間 42 mstr = malloc(strlen(src) +1); 43 //拷貝src到mstr 44 strcpy(mstr,src); 45 //調用reverse方法,逆序字符串 46 reverse(mstr); 47 //這里把原字符串和轉換后的字符串返回 48 retval = (PyObject *) Py_BuildValue("ss",src,mstr); 49 //釋放空間 50 free(mstr); 51 52 return retval; 53 } 54 55 56 //為test函數設置包裹函數 57 static PyObject *py_test1_test(PyObject *self,PyObject *args) 58 { 59 //直接調用c函數 60 test(); 61 62 return (PyObject *)Py_BuildValue(""); 63 } 64 65 66 //添加模塊數組(注意是PyMethodDef,不要錯寫成PyMethondDef) 67 //定義對應的方法名,后面Python調用的時候就用這里面的方法名調用 68 static PyMethodDef py_test1Methods[] = { 69 {"fac",py_test1_fac,METH_VARARGS}, 70 {"doppel",py_test1_doppel,METH_VARARGS}, 71 {"test",py_test1_test,METH_VARARGS}, 72 {NULL,NULL}, 73 }; 74 75 76 //模塊初始化函數 77 void initpy_test1(void) 78 { 79 Py_InitModule("py_test1",py_test1Methods); 80 }
到此為止,准備工作可以說已經完成了,接下來就需要編譯、安裝上面的那些文件了,怎么編譯呢? - 編譯、安裝和測試
一、編譯安裝
1.創建setup.py文件,文件內容也很簡單,主要功能就是使用python的自帶模塊,將包裹文件編譯。1 #incoding:utf-8 2 from distutils.core import setup,Extension 3 #模塊名 4 MOD = 'py_test1' 5 #資源(要編譯和鏈接的代碼文件) 6 source = ['py_test1.c','py_test1wrapper.c'] 7 8 #調用setup函數,編譯和鏈接 9 setup(name=MOD,ext_modules=[Extension(MOD,sources=source)])
那如果運行這個setup.py 程序呢?這里就不能直接build了,需要到命令行里操作,定位要當前目錄后通過命令python setup.py build來編譯setup.py文件(出現各種錯誤,如:error: Unable to find vcvarsall.bat請看文章最后)
2.編譯完成過后,可以在當前文件目錄下找到一個build文件夾,里面就是編譯過后的內容了,我們也無需知道里面到底是些什么文件。編譯完成我們怎么使用?別急,我們還要安裝它們到python的庫中,同樣通過命令python setup.py install安裝。
這里其實就可以發現其實是往我們本地python庫中安裝了文件,也就是一個模塊。在python安裝目錄下Lib目錄的site-packages文件夾下。現在我們就可以使用它了。
二、測試
現在就可以使用python來調用最開始寫的那個c程序了,別忘了要導入模塊哦~
測試程序test.py:
1 #incoding:utf-8 2 import py_test1 3 print help(py_test1) #查看里面都要哪些方法 4 py_test1.test() #調用test函數 5 print "-"*50 6 print py_test1.fac(9) #調用fac()求階乘的函數 7 print py_test1.doppel("yycsetup") #調用逆序函數
運行輸出:
1 Help on module py_test1: 2 3 NAME 4 py_test1 5 6 FILE 7 d:\python27\lib\site-packages\py_test1.pyd 8 9 FUNCTIONS 10 doppel(...) 11 12 fac(...) 13 14 test(...) 15 16 17 None 18 5! = 120 19 10! = 3628800 20 reversing 'hello world',we get 'dlrow olleh' 21 -------------------------------------------------- 22 362880 23 ('yycsetup', 'putescyy') 24 [Finished in 0.5s]
- 錯誤解決
錯誤提示error: Unable to find vcvarsall.bat
解決地址 : 其實這個錯就是缺少文件vcvarsall.bat,基本解決思路就是安裝VCForPython27(安裝過后可能提示找不到vc),修改python源代碼(修改方法返回值)。下面這幾個地址值得參考,標紅的為重要解決思路。
------------https://www.cnblogs.com/yyds/p/7065637.html
------------安裝VCForPython27:https://www.microsoft.com/en-us/download/details.aspx?id=44266
------------升級setuptools:https://pypi.org/project/setuptools/
------------設置setuptools環境變量:https://www.cnblogs.com/fbwfbi/p/4509622.html
------------修改方法返回值:https://www.cnblogs.com/lazyboy/p/4017567.html