C语言的Python(C API)

主要的

去年,需要 Python3中的功能来补充用C编写的旧项目。 尽管事实上有关于此主题的文章,但我在当年和现在写这篇文章的程序时都受了苦。 因此,我将给出一些示例,说明如何在Linux下使用C使用Python3(使用我的用法)。 我将描述如何创建一个类并调用其方法来访问变量。 调用函数并从模块获取变量。 还有我遇到的无法理解的问题。


配套


我们使用标准的Python API forC。 所需的Python软件包:


  • python3
  • python3-dev
  • python3-all
  • python3-all-dev
  • libpython3-all-dev

在口译员中工作


最简单的事情是在Python解释器中加载和工作。


要工作,必须连接头文件:


#include <Python.h> 

加载解释器:


 Py_Initialize(); 

接下来是Python的一部分工作,例如:


 PyRun_SimpleString("print('Hello!')"); 

卸载解释器:


 Py_Finalize(); 

完整示例:


 #include <Python.h> void main() { //   Python Py_Initialize(); //     PyRun_SimpleString("print('Hello!')"); //   Python Py_Finalize(); } 

如何编译和运行:


 gcc simple.c $(python3-config --includes --ldflags) -o simple && ./simple Hello! 

但这不起作用:


 gcc $(python3-config --includes --ldflags) simple.c -o simple && ./simple /tmp/ccUkmq57.o: In function `main': simple.c:(.text+0x5): undefined reference to `Py_Initialize' simple.c:(.text+0x16): undefined reference to `PyRun_SimpleStringFlags' simple.c:(.text+0x1b): undefined reference to `Py_Finalize' collect2: error: ld returned 1 exit status 

这是因为python3-config --includes --ldflags扩展为这种类型的东西:


 -I/usr/include/python3.6m -I/usr/include/python3.6m -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions 

在这里,我认为-Wl链接器的连接顺序很重要 。 谁能更确切地知道,在评论中写些,我将补充答案。


MooNDeaR的 解释


一切都非常简单-一次搜索字符,所有未使用的字符都将被丢弃。 如果将simple.c放在最后,结果表明链接器在查看python库后将看到Py_Initialize()符号的使用,此刻所有符号都将被丢弃(因为未使用它们)。

从Python文件调用函数的示例:
简单的


 #include <Python.h> void python() { //   Python Py_Initialize(); //     //   sys PyRun_SimpleString("import sys"); //    python PyRun_SimpleString("sys.path.append('./src/python')"); PyRun_SimpleString("import simple"); PyRun_SimpleString("print(simple.get_value(2))"); PyRun_SimpleString("print(simple.get_value(2.0))"); PyRun_SimpleString("print(simple.get_value(\"Hello!\"))"); //   Python Py_Finalize(); } void main() { puts("Test simple:"); python(); } 

simple.py


 #!/usr/bin/python3 #-*- coding: utf-8 -*- def get_value(x): return x 

但是这些都是简单而有趣的事情,我们无法获得函数的结果。


使用函数和模块变量


这有点棘手。
将Python解释器和func.py模块加载到其中:


 PyObject * python_init() { //   Python Py_Initialize(); do { //   sys sys = PyImport_ImportModule("sys"); sys_path = PyObject_GetAttrString(sys, "path"); //     Python folder_path = PyUnicode_FromString((const char*) "./src/python"); PyList_Append(sys_path, folder_path); //  func.py pName = PyUnicode_FromString("func"); if (!pName) { break; } //    pModule = PyImport_Import(pName); if (!pModule) { break; } //      pDict = PyModule_GetDict(pModule); if (!pDict) { break; } return pDict; } while (0); //   PyErr_Print(); } 

释放Python解释器资源:


 void python_clear() { //    Py_XDECREF(pDict); Py_XDECREF(pModule); Py_XDECREF(pName); Py_XDECREF(folder_path); Py_XDECREF(sys_path); Py_XDECREF(sys); //   Python Py_Finalize(); } 

使用变量和模块功能。


 /** *          */ char * python_func_get_str(char *val) { char *ret = NULL; //   get_value  func.py pObjct = PyDict_GetItemString(pDict, (const char *) "get_value"); if (!pObjct) { return ret; } do { //  pObjct  . if (!PyCallable_Check(pObjct)) { break; } pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal); //     ,     Python   . //   pResultRepr     . ret = strdup(PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR"))); Py_XDECREF(pResultRepr); Py_XDECREF(pVal); } else { PyErr_Print(); } } while (0); return ret; } /** *       int */ int python_func_get_val(char *val) { int ret = 0; //     val pVal = PyDict_GetItemString(pDict, (const char *) val); if (!pVal) { return ret; } //    long if (PyLong_Check(pVal)) { ret = _PyLong_AsInt(pVal); } else { PyErr_Print(); } return ret; } 

让我们更详细地讨论这一点。


 pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); 

“(s)”表示将1个char *参数作为参数传递给get_value(x) 。 如果我们需要将几个参数传递给该函数,则将如下所示:


 pVal = PyObject_CallFunction(pObjct, (char *) "(sss)", val1, val2, val3); 

如果您需要传递int ,则将使用字母i ,所有可能的数据类型及其名称都可以在Python 文档中找到。


 pVal = PyObject_CallFunction(pObjct, (char *) "(i)", my_int); 

func.py:


 #!/usr/bin/python3 #-*- coding: utf-8 -*- a = 11 b = 22 c = 33 def get_value(x): return x def get_bool(self, x): if x: return True else: return False 

问题已解决
我遇到且无法理解的问题:


 int main() { puts("Test func:"); if (!python_init()) { puts("python_init error"); return -1; } puts("Strings:"); printf("\tString: %s\n", python_func_get_str("Hello from Python!")); puts("Attrs:"); printf("\ta: %d\n", python_func_get_val("a")); printf("\tb: %d\n", python_func_get_val("b")); printf("\tc: %d\n", python_func_get_val("c")); python_clear(); return 0; } 

如果我想从func.py获取bc ,则打开:


 Py_Finalize(); 

我遇到了细分错误 。 仅获得一个,就没有这种问题。
接收类变量时,也没有问题。


pwl的 解释


PyObject PyDict_GetItemString(PyObject p,const char *键)
返回值:借用的参考。 无需做任何借用的参考。

问题是我在为PyDict_GetItemString()调用Py_XDECREF() 。 无需为此功能执行此操作,从而导致分段错误


班级工作


仍然有些复杂。
将Python解释器和class.py模块加载到其中。


 PyObject * python_init() { //   Python Py_Initialize(); do { //   sys sys = PyImport_ImportModule("sys"); sys_path = PyObject_GetAttrString(sys, "path"); //     Python folder_path = PyUnicode_FromString((const char*) "./src/python"); PyList_Append(sys_path, folder_path); //  Unicode   UTF-8  pName = PyUnicode_FromString("class"); if (!pName) { break; } //   class pModule = PyImport_Import(pName); if (!pModule) { break; } //      pDict = PyModule_GetDict(pModule); if (!pDict) { break; } //   Class  class.py pClass = PyDict_GetItemString(pDict, (const char *) "Class"); if (!pClass) { break; } //  pClass  . if (!PyCallable_Check(pClass)) { break; } //   Class pInstance = PyObject_CallObject(pClass, NULL); return pInstance; } while (0); //   PyErr_Print(); } 

传递字符串作为参数并返回字符串


 char * python_class_get_str(char *val) { char *ret = NULL; pVal = PyObject_CallMethod(pInstance, (char *) "get_value", (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal); //     ,     Python   . ret = strdup(PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR"))); Py_XDECREF(pResultRepr); Py_XDECREF(pVal); } else { PyErr_Print(); } return ret; } 

这里没有问题,一切正常,没有错误。 源代码中有一些示例如何使用intdoublebool


在编写材料时,我刷新了学识)
我希望它会有用。


参考文献


Source: https://habr.com/ru/post/zh-CN466181/


All Articles