程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> python與c互相調用

python與c互相調用

編輯:Python

  雖然python開發效率很高,但作為腳本語言,其性能不高,所以為了兼顧開發效率和性能,通常把性能要求高的模塊用c或c++來實現或者在c或c++中運行python腳本來處理邏輯,前者通常是python中一些模塊的實現方式,後者服務端程序(實現業務擴展或是Plugin功能)和游戲開發(腳本只處理邏輯)中比較常見。本文主要介紹通過在c中運行python腳本來實現python與c的相互調用,並通過c和python腳本設置同一段內存區域為例子來講解。

 

准備工作

  為了在c中運行python腳本,需要在程序鏈接的時候將python虛擬機庫鏈接進去,python虛擬機庫是python安裝目錄下libs中的python27.lib文件,至於怎樣將庫鏈接進程序中可以自己google下。由於在c中使用了python的一些方法和數據結構,所以需要將python安裝目錄下的include目錄添加到項目include目錄中。好了,需要准備的就是這些,然後就可以開始實現一個設置內存區域的例子了。

 

內嵌python虛擬機

  在c中內嵌python虛擬機很簡單,只需要在程序開頭include Python.h頭文件,然後調用下面兩段來初始化python虛擬機實例就行了。

 Py_SetPythonHome("D:\Python27");
 Py_Initialize();

  Py_SetPythonHome函數是用來設置python的庫路徑,也就是python安裝路徑,Py_Initialize函數真正實例化一個python虛擬機,這樣就把一個python虛擬機內嵌到c中了。

 

調用python腳本

   將python虛擬機初始化後,其實就可以調用python腳本了。c中調用腳本模塊中的方法分下面幾個步驟:

  1、使用PyImport_ImportModule導入腳步模塊;

  2、使用PyObject_GetAttrString獲取模塊特定方法信息;

  3、使用Py_VaBuildValue轉換輸入參數;

  4、使用PyObject_CallObject調用特定方法;

  5、使用PyArg_Parse轉換方法的返回結果。

  由於上面流程在調用模塊中的方法都是必須的,所以可以寫個函數來封裝上面的5個步驟,具體代碼如下:

 int PyModuleRunFunction(const char *module, const char *function,
                         const char *result_format, void *result, const char *args_format, ...)
 {
 
     PyObject *pmodule, *pfunction, *args, *presult;
 
     pmodule = PyImport_ImportModule(const_cast<char *>(module));
     if (!pmodule)
     {
         PyObject *type = PyErr_Occurred();
         if (type == PyExc_NameError)
         {
             PyErr_Clear();
             return 0;
         }
 
         PyError("PyModuleRunFunction");
         return -1;
     }
 
     pfunction = PyObject_GetAttrString(pmodule, const_cast<char *>(function));
     Py_DECREF(pmodule);
     if (!pfunction)
     {
         PyObject *type = PyErr_Occurred();
         if (type == PyExc_AttributeError)
         {
             PyErr_Clear();
             return 0;
         }
 
         PyError("PyModuleRunFunction");
         return -2;
     }
 
     if (pfunction == Py_None)
     {
         return 0;
     }
 
     va_list args_list;
     va_start(args_list, args_format);
     args = Py_VaBuildValue(const_cast<char *>(args_format), args_list);
     va_end(args_list);
 
     if (!args)
     {
         Py_DECREF(pfunction);
         return -3;
     }
 
     presult = PyObject_CallObject(pfunction, args);
     if (presult == 0)
     {
         PyError("PyModuleRunFunction");
         Py_XDECREF(pfunction);
         Py_XDECREF(args);
         return -1;
     }
 
     Py_XDECREF(pfunction);
     Py_XDECREF(args);
 
     return ConvertResult(presult, result_format, result);
 }
View Code

  有了上面的調用python模塊內方法的通用函數,我們就可以直接調用python腳本中的方法了,具體如下:

1 PyModuleRunFunction("hello", "test", "", 0, "()");

   這樣我們就實現了再c中調用python的方法。下面我們再來開心python怎麼調用c中的方法。

 

初始化c實現的python模塊

   為了能在python腳本中調用到c中定義的方法,需要先在c中定義一個python模塊,然後在腳本中import這個模塊,最後通過這個模塊來間接調用c中定義的方法。例如,我們通過c定義了一塊內存區域data和對這個內存區域操作的函數SetData與GetData(代碼如下),怎樣在腳本中調用SetData與GetData函數來操作data呢?其實關鍵問題是怎麼樣在腳本中調用SetData和GetData函數,如果能在腳本中調用這兩個函數,自然就能操作data了。python中通過模塊的方式來解決這個問題。

 #define min(a,b)    (((a) < (b)) ? (a) : (b))
 
 char data[1024];
 
 void SetData(const char *str)
 {
     strncpy(data, str, min(strlen(str) + 1, 1024));
 }
 
 const char *GetData()
 {
     return data;
 }

  在c中定義一個python模塊有特定的步驟,具體代碼如下:

 PyDoc_STRVAR(PySetData_doc__, "\
 測試\n \n PySetData(str)\n str: 出入的字符串\n 返回: \n null \n ");
 static PyObject* PySetData(PyObject *self, PyObject *args)
 {
     const char* str = NULL;
     if ( !PyArg_ParseTuple(args, "s", &str) )
     {
         return 0;
     }
     SetData(str);
     Py_RETURN_NONE;
 }
 
 PyDoc_STRVAR(PyGetData_doc__, "\
 打印數據\n \n PyGetData()\n 返回: \n data \n ");
 static PyObject* PyGetData(PyObject *self, PyObject *args)
 {
     const char* str = NULL;
     return PyString_FromString(GetData());
 }
 
 static PyMethodDef module_methods[] = {
     {"py_set_data", PySetData, METH_VARARGS, PySetData_doc__},
     {"py_get_data", PyGetData, METH_VARARGS, PyGetData_doc__},
     {NULL}
     };
 void InitCCallPy()
 {
     PyObject *module = Py_InitModule3("pycallc", module_methods,
         "python call c");
 }
View Code

  Py_InitModule3用來定義一個python模塊,第一個參數是模塊的名字,第二個參數是模塊中的方法描述集合,第三個參數是模塊的描述信息。上面代碼中我們定義了一個叫pycallc的模塊,方法描述集合module_methods描述了兩個方法py_set_data和py_get_data,這兩個方法對應的函數地址是PySetData和PyGetData,這兩個函數最終會分別調用前面定義的SetData和GetData。這樣我們在python腳本中通過pycallc模塊的py_set_data和py_get_data方法就可以設置和獲取data數據了。看了上面的實現,其實這個python模塊的主要作用就是把c中定義的函數再封裝一次,封裝的函數能夠被python識別。

 

在python腳本中調用c實現的python模塊

   由於前面已經通過c代碼初始化了一個python模塊pycallc,那麼在腳本中我們就可以通過import導入這個模塊,並調用這個模塊中的函數。具體代碼如下:

 # -*- coding: utf-8 -*-
 
 import pycallc
 
 def test():
     print 'in python : ', pycallc.py_get_data()
     pycallc.py_set_data("change hello world!")

   這樣我們就實現了在python腳本中調用c中的方法。

  上面完整的代碼demo的鏈接:https://github.com/morningstatus/python/tree/master/ccallpy

 

總結

  從上面c調用python,python調用c,其實都是一些固定的步驟,知道就會用了,沒有會不會的問題,只有想不想知道的問題。沒有接觸這個技術前可能覺得它很高深,但其實只要稍微花點心思去了解它,它也其實沒有這麼難。計算機很多技術不外乎都是這樣,只有你想不想的問題,沒有你會不會的問題,多問,多思考,多學習,總有一天你也能成為技術大牛。

 

參考

  python官方:https://docs.python.org/2/c-api/index.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved