2010-07-20 122 views
34

我想調用一個自定義函數,該函數在C的python模塊中定義。我有一些初步代碼來做到這一點,但它只是打印輸出到標準輸出。從C/C++調用python方法,並提取其返回值

mytest.py

import math 

def myabs(x): 
    return math.fabs(x) 

TEST.CPP

#include <Python.h> 

int main() { 
    Py_Initialize(); 
    PyRun_SimpleString("import sys; sys.path.append('.')"); 
    PyRun_SimpleString("import mytest;"); 
    PyRun_SimpleString("print mytest.myabs(2.0)"); 
    Py_Finalize(); 

    return 0; 
} 

我如何提取的返回值到C雙鍵和C使用它?

+2

你看過這樣的:HTTP:// docs.python.org/c-api/?它似乎回答你的問題。 http://docs.python.org/c-api/number.html#PyNumber_Float似乎是你要找的。它出什麼問題了?你還需要什麼? – 2010-07-20 02:04:13

+1

真正的問題是如何從'mytest.myabs(2.0)'訪問返回的對象。一旦我有了一個指針,我可以使用PyNumber_Float函數輕鬆地將其轉換爲浮點數。 – dzhelil 2010-07-20 02:16:09

+1

我們只能看到一個代碼示例的答案,並用它來完成嗎? – 2010-07-21 00:16:21

回答

51

如前所述,使用PyRun_SimpleString似乎是一個壞主意。

您一定要使用C-API(http://docs.python.org/c-api/)提供的方法。

閱讀介紹是理解其工作方式的第一步。

首先,您必須瞭解PyObject是C API的基本對象。它可以表示任何類型的Python基本類型(string,float,int,...)。

許多函數用於將例如python字符串轉換爲char *或PyFloat以加倍。

首先,導入你的模塊:

PyObject* myModuleString = PyString_FromString((char*)"mytest"); 
PyObject* myModule = PyImport_Import(myModuleString); 

然後讓你的函數的引用:

PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"myabs"); 
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(2.0)); 

然後讓你的結果:

PyObject* myResult = PyObject_CallObject(myFunction, args) 

,能重返雙:

double result = PyFloat_AsDouble(myResult); 

你應該明顯地檢查錯誤(參見由Mark Tolonen給出)。

如果您有任何問題,請不要猶豫。祝你好運。

+4

我會接受你的答案,因爲有用的提示,但如果你可以提供一個完整的工作C模塊調用上面的python函數,它會對我更有幫助。 – dzhelil 2010-08-01 02:51:32

+0

如果您正在調用對象的類方法,是否需要在參數中傳遞該對象? – 2016-02-02 18:57:24

+4

儘管它被接受,但這個答案很不幸並不是調用Python代碼的好模板(並且我已經在其他地方參考過它)。每次執行時,'PyTuple_Pack'行會泄漏一個'float'對象。該函數可以在不進行手動轉換和元組創建的情況下調用,使用'result = PyObject_CallFunction(myFunction,「d」,2.0)'。導入可以寫成'module = PyImport_ImportModule(「mytest」)'(不創建Python字符串)。 – user4815162342 2016-08-30 06:47:35

1

您必須以某種方式提取python方法並使用PyObject_CallObject()運行該方法。爲此,您可以使用Python來設置函數,如Extending and Embedding Python Tutorial示例所做的那樣。

6

調用一個Python功能和檢索結果位於http://docs.python.org/release/2.6.5/extending/embedding.html#pure-embedding的一個完整的例子:

#include <Python.h> 

int 
main(int argc, char *argv[]) 
{ 
    PyObject *pName, *pModule, *pDict, *pFunc; 
    PyObject *pArgs, *pValue; 
    int i; 

    if (argc < 3) { 
     fprintf(stderr,"Usage: call pythonfile funcname [args]\n"); 
     return 1; 
    } 

    Py_Initialize(); 
    pName = PyString_FromString(argv[1]); 
    /* Error checking of pName left out */ 

    pModule = PyImport_Import(pName); 
    Py_DECREF(pName); 

    if (pModule != NULL) { 
     pFunc = PyObject_GetAttrString(pModule, argv[2]); 
     /* pFunc is a new reference */ 

     if (pFunc && PyCallable_Check(pFunc)) { 
      pArgs = PyTuple_New(argc - 3); 
      for (i = 0; i < argc - 3; ++i) { 
       pValue = PyInt_FromLong(atoi(argv[i + 3])); 
       if (!pValue) { 
        Py_DECREF(pArgs); 
        Py_DECREF(pModule); 
        fprintf(stderr, "Cannot convert argument\n"); 
        return 1; 
       } 
       /* pValue reference stolen here: */ 
       PyTuple_SetItem(pArgs, i, pValue); 
      } 
      pValue = PyObject_CallObject(pFunc, pArgs); 
      Py_DECREF(pArgs); 
      if (pValue != NULL) { 
       printf("Result of call: %ld\n", PyInt_AsLong(pValue)); 
       Py_DECREF(pValue); 
      } 
      else { 
       Py_DECREF(pFunc); 
       Py_DECREF(pModule); 
       PyErr_Print(); 
       fprintf(stderr,"Call failed\n"); 
       return 1; 
      } 
     } 
     else { 
      if (PyErr_Occurred()) 
       PyErr_Print(); 
      fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]); 
     } 
     Py_XDECREF(pFunc); 
     Py_DECREF(pModule); 
    } 
    else { 
     PyErr_Print(); 
     fprintf(stderr, "Failed to load \"%s\"\n", argv[1]); 
     return 1; 
    } 
    Py_Finalize(); 
    return 0; 
} 
+0

這工作在2分鐘內,非常感謝! – jhegedus 2015-09-13 07:56:49

1

如果返回值賦值給一個變量,那麼你可以使用像PyEval_GetGlobals()和PyDict_GetItemString( )來獲取PyObject。從那裏,PyNumber_Float可以爲你提供你想要的價值。

我建議瀏覽整個API--當你看到可用的不同方法時,某些事情會變得很明顯,並且可能會有比我描述的更好的方法。

0

我一直在使用BOOST到嵌入式Python到C它做++ [該工作C模塊中應幫助]

#include <boost/python.hpp> 

void main() 
{ 
using namespace boost::python; 
Py_Initialize(); 
PyObject* filename = PyString_FromString((char*)"memory_leak_test"); 
    PyObject* imp = PyImport_Import(filename); 
    PyObject* func = PyObject_GetAttrString(imp,(char*)"begin"); 
    PyObject* args = PyTuple_Pack(1,PyString_FromString("CacheSetup")); 
    PyObject* retured_value = PyObject_CallObject(func, args); // if you have arg 
    double retured_value = PyFloat_AsDouble(myResult); 
std::cout << result << std::endl; 
Py_Finalize(); 
} 
+0

這個腳本中的提升部分在哪裏? – Antonello 2016-02-24 09:22:44

12

這裏是一個示例代碼,我寫的(有各種在線資源的幫助下)發送一個字符串到一個Python代碼,然後返回一個值。

這裏是C代碼call_function.c

#include <Python.h> 
#include <stdlib.h> 
int main() 
{ 
    // Set PYTHONPATH TO working directory 
    setenv("PYTHONPATH",".",1); 

    PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult; 


    // Initialize the Python Interpreter 
    Py_Initialize(); 


    // Build the name object 
    pName = PyString_FromString((char*)"arbName"); 

    // Load the module object 
    pModule = PyImport_Import(pName); 


    // pDict is a borrowed reference 
    pDict = PyModule_GetDict(pModule); 


    // pFunc is also a borrowed reference 
    pFunc = PyDict_GetItemString(pDict, (char*)"someFunction"); 

    if (PyCallable_Check(pFunc)) 
    { 
     pValue=Py_BuildValue("(z)",(char*)"something"); 
     PyErr_Print(); 
     printf("Let's give this a shot!\n"); 
     presult=PyObject_CallObject(pFunc,pValue); 
     PyErr_Print(); 
    } else 
    { 
     PyErr_Print(); 
    } 
    printf("Result is %d\n",PyInt_AsLong(presult)); 
    Py_DECREF(pValue); 

    // Clean up 
    Py_DECREF(pModule); 
    Py_DECREF(pName); 

    // Finish the Python Interpreter 
    Py_Finalize(); 


    return 0; 
} 

這裏是Python代碼,在文件arbName.py

def someFunction(text): 
    print 'You passed this Python program '+text+' from C! Congratulations!' 
    return 12345 

我使用命令gcc call_function.c -I/usr/include/python2.6 -lpython2.6 ; ./a.out運行此過程。我在redhat上。我建議使用PyErr_Print();進行錯誤檢查。

+0

@ Palec-我無法弄清楚你改變了什麼。你能幫我看看我的錯誤嗎? – kilojoules 2014-07-11 04:37:43

+2

調用'a.out'會比'.a.out'更好,因爲你對'a.out'的調用依賴於PATH中的工作目錄,這是不常見的配置。您還應該考慮向GCC提供'-o'選項,以便爲可執行文件提供更好的名稱。 – Palec 2014-07-11 08:22:08

+1

請參閱Meta SE上的[編輯如何工作?](http://meta.stackexchange.com/a/21789/238706)。如果您可以訪問修訂歷史記錄,但無法看到標記中的更改,只需將差異視圖切換爲「並排降價」即可。修訂歷史記錄中每個修訂版本的頂部都有按鈕,用於切換差異視圖。如果您不明白標記的作用,請參閱[編輯幫助](http://stackoverflow.com/editing-help)。如果您正在編輯帖子,則編輯器具有內置幫助 - 查看編輯器右上方的橙色問號。 – Palec 2014-07-11 08:26:20

2

爲了防止多餘的.py文件在其他的答案,你可以檢索__main__模塊,這是第一次調用創建PyRun_SimpleString

PyObject *moduleMainString = PyString_FromString("__main__"); 
PyObject *moduleMain = PyImport_Import(moduleMainString); 

PyRun_SimpleString(
    "def mul(a, b):         \n"\ 
    " return a * b        \n"\ 
); 

PyObject *func = PyObject_GetAttrString(moduleMain, "mul"); 
PyObject *args = PyTuple_Pack(2, PyFloat_FromDouble(3.0), PyFloat_FromDouble(4.0)); 

PyObject *result = PyObject_CallObject(func, args); 

printf("mul(3,4): %.2f\n", PyFloat_AsDouble(result)); // 12