Python基礎筆記系列十四:python無縫調用c程序


  本系列教程供個人學習筆記使用,如果您要瀏覽可能需要其它編程語言基礎(如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 }
    c源程序

    二、創建.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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM