python - 如何将数组从 C 传递到嵌入式 python 脚本

标签 python embedding python-c-api

我遇到了一些问题,需要一些帮助。我有一段代码,用于嵌入 python 脚本。这个 python 脚本包含一个函数,它期望接收一个数组作为参数(在这种情况下,我在 python 脚本中使用 numpy 数组)。
我想知道如何将数组从 C 传递到嵌入式 python 脚本作为脚本中函数的参数。更具体地说,有人可以向我展示一个简单的例子。

最佳答案

真的,这里最好的答案可能是使用 numpy数组,甚至来自您的 C 代码。但如果这是不可能的,那么您将面临与在 C 类型和 Python 类型之间共享数据的任何代码相同的问题。

一般来说,C 和 Python 之间共享数据至少有五个选项:

  • 创建 Python list或其他要通过的对象。
  • 定义一个新的 Python 类型(在您的 C 代码中)来包装和表示数组,使用您在 Python 中为序列对象定义的相同方法( __getitem__ 等)。
  • 将指向数组的指针强制转换为 intptr_t , 或显式 ctypes输入,或者只是让它不强制转换;然后使用 ctypes在 Python 端访问它。
  • 将指向数组的指针强制转换为 const char *并将其作为 str 传递(或者,在 Py3 中, bytes ),并使用 structctypes在 Python 端访问它。
  • 创建与 buffer 匹配的对象协议(protocol),并再次使用 structctypes在 Python 方面。

  • 在您的情况下,您想使用 numpy.array在 Python 中。所以,一般情况变成:
  • 创建一个 numpy.array通过。
  • (可能不合适)
  • 将指针按原样传递给数组,并在 Python 中使用 ctypes使其成为 numpy 的类型可以转换成数组。
  • 将指向数组的指针强制转换为 const char *并将其作为 str 传递(或者,在 Py3 中, bytes ),这已经是 numpy 的类型可以转换成数组。
  • 创建与 buffer 匹配的对象协议(protocol),我再次相信 numpy可以直接转换。

  • 对于 1,这里是如何使用 list ,只是因为它是一个非常简单的例子(而且我已经写过了……):
    PyObject *makelist(int array[], size_t size) {
        PyObject *l = PyList_New(size);
        for (size_t i = 0; i != size; ++i) {
            PyList_SET_ITEM(l, i, PyInt_FromLong(array[i]));
        }
        return l;
    }
    

    这是numpy.array等效(假设您可以依赖 C array 不被删除—请参阅文档中的 Creating arrays 以获取有关此处选项的更多详细信息):
    PyObject *makearray(int array[], size_t size) {
        npy_int dim = size;
        return PyArray_SimpleNewFromData(1, &dim, (void *)array);
    }
    

    无论如何,不​​管你怎么做,你最终都会得到一个看起来像 PyObject * 的东西。来自 C(并且有一个引用计数),因此您可以将其作为函数参数传递,而在 Python 方面,它看起来像 numpy.array , list , bytes ,或其他任何合适的。

    现在,您实际上如何传递函数参数?那么,Pure Embedding中的示例代码您在评论中引用的内容显示了如何执行此操作,但并没有真正解释发生了什么。实际上,扩展文档中的解释比嵌入文档要多,特别是 Calling Python Functions from C .另外,请记住 standard library source code充满了这方面的例子(尽管其中一些没有达到应有的可读性,要么是因为优化,要么只是因为它们尚未更新以利用新的简化的 C API 功能)。

    跳过关于从 Python 获取 Python 函数的第一个示例,因为大概你已经有了它。第二个例子(以及关于它的段落)展示了一个简单的方法:使用 Py_BuildValue 创建一个参数元组.因此,假设我们要调用您存储在 myfunc 中的函数。与列表 mylist由该返回 makelist以上功能。这是你要做的:
    if (!PyCallable_Check(myfunc)) {
        PyErr_SetString(PyExc_TypeError, "function is not callable?!");
        return NULL;
    }
    PyObject *arglist = Py_BuildValue("(o)", mylist);
    PyObject *result = PyObject_CallObject(myfunc, arglist);
    Py_DECREF(arglist);
    return result;
    

    当然,如果您确定拥有有效的可调用对象,则可以跳过可调用检查。 (如果合适的话,通常最好检查您第一次获得 myfunc 的时间,因为这样可以提供更早和更好的错误反馈。)

    如果您想真正了解发生了什么,请在没有 Py_BuildValue 的情况下尝试.正如文档所说,[PyObject_CallObject][6] 的第二个参数是一个元组,而 PyObject_CallObject(callable_object, args)相当于 apply(callable_object, args) ,相当于 callable_object(*args) .所以,如果你想拨打 myfunc(mylist)在 Python 中,您必须将其有效地转换为 myfunc(*(mylist,))所以你可以把它翻译成C。你可以构造一个tuple像这样:
    PyObject *arglist = PyTuple_Pack(1, mylist);
    

    但通常,Py_BuildValue更容易(特别是如果您还没有将所有内容打包为 Python 对象),并且代码中的意图更清晰(就像使用 PyArg_ParseTuple 比在另一个方向使用显式 tuple 函数更简单和更清晰)。

    那么,你是如何得到的myfunc ?好吧,如果您已经从嵌入代码创建了该函数,只需保留指针即可。如果您希望它从 Python 代码传入,这正是第一个示例所做的。如果你想,例如,从模块或其他上下文中按名称查找它,具体类型的 API,如 PyModule 和抽象类型,如 PyMapping 非常简单,通常很明显如何将 Python 代码转换为等效的 C 代码,即使结果大多是丑陋的样板。

    把它们放在一起,假设我有一个 C 整数数组,我想要 import mymodule并调用函数 mymodule.myfunc(mylist)返回一个int。这是一个精简的示例(未经实际测试,也没有错误处理,但它应该显示所有部分):
    int callModuleFunc(int array[], size_t size) {
        PyObject *mymodule = PyImport_ImportModule("mymodule");
        PyObject *myfunc = PyObject_GetAttrString(mymodule, "myfunc");
        PyObject *mylist = PyList_New(size);
        for (size_t i = 0; i != size; ++i) {
            PyList_SET_ITEM(l, i, PyInt_FromLong(array[i]));
        }
        PyObject *arglist = Py_BuildValue("(o)", mylist);
        PyObject *result = PyObject_CallObject(myfunc, arglist);
        int retval = (int)PyInt_AsLong(result);
        Py_DECREF(result);
        Py_DECREF(arglist);
        Py_DECREF(mylist);
        Py_DECREF(myfunc);
        Py_DECREF(mymodule);
        return retval;
    }
    

    如果您使用 C++,您可能想研究某种范围保护/看门人/等。处理所有这些 Py_DECREF调用,尤其是当您开始进行适当的错误处理时(这通常意味着早期的 return NULL 调用会在函数中出现)。如果您使用 C++11 或 Boost,unique_ptr<PyObject, Py_DecRef>可能就是你所需要的。

    但实际上,如果您打算进行大量 C<->Python 通信,则减少所有丑陋样板的更好方法是查看所有为改进 Python 扩展而设计的熟悉框架— Cython , boost::python等。即使您在嵌入,您也可以有效地执行与扩展相同的工作,因此它们可以以相同的方式提供帮助。

    就此而言,如果您搜索文档,其中一些还提供工具来帮助嵌入部分。例如,您可以在 Cython 中编写主程序,使用 C 代码和 Python 代码,以及 cython --embed .你可能想要交叉手指和/或牺牲一些鸡,但如果它有效,它会非常简单和高效。 Boost 的开始并不是那么简单,但是一旦你把事情放在一起,几乎所有的事情都会按照你期望的方式完成,并且可以正常工作,这对于 embedding 也是如此。作为延伸。等等。

    关于python - 如何将数组从 C 传递到嵌入式 python 脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13942128/

    相关文章:

    python - PyQt 的 Signal/SLOT 不同类

    python - 如何根据值更改Excel单元格颜色?

    python - 嵌入Python线程安全

    javascript - 如何将Instagram嵌入特定语言?

    python - 我如何找出使用 PyImportModule 导入失败的原因?

    c++ - 将一段 C++ 代码包装成 Python

    python - makecython++导致 fatal error : Python. h:尽管安装了python3-dev,但没有这样的文件或目录

    python - 根据另一个引用追加

    python - 拉伸(stretch)因子在 Qt 中是如何工作的?

    css - 嵌入页面的独立样式