【问题标题】:Compile python program to library importable in C将 python 程序编译为可在 C 中导入的库
【发布时间】:2018-01-22 01:00:55
【问题描述】:

我今天开始wondering whether it is possible to save a python object for use in a C program,这个命题在阅读数小时后看起来很幼稚。这是一个可能的解决方法:

1。创建一个复杂的对象,该对象依赖于许多 python 库,其中包含我需要保留的数据。
2. 腌制复杂对象并将其放置在可以访问的地方。
3.定义compileme.py:

import pickle
thing = pickle.load(open('thing.pkl', 'r'))# an object with a method query(),
                                           # which takes a numpy array as input

4。 cython --embed -o compileme.c compileme.py 生成脚本的 .c 版本。
5.定义main.c:

#include <stdio.h>
#include//(A) something from compileme

int main(void) {
    input = //(B) query takes a numpy array in python. Define something palatable.
    double result = thing.query(input);
    printf("%d", result);
}

6。使用所有正确的链接正确编译 main.c。

我不清楚这个基本的解决策略是否合理,我有一些担忧:

  1. thing 是这里甚至没有提到的库中的一个类,所以它的query() 方法依赖于外部 python。如何确保相关部分也被编译和链接?
  2. 我应该如何在我的main.c 中包含compileme 以便在那里可以访问thing? (代码中的位置 (A))
  3. 如何在此处适当地定义thing 方法的输入?我需要使用compileme.c 中定义的众多类型之一吗? (代码中的位置 (B))
  4. 如何使用正确的链接编译main.c
  5. 在执行所有这些操作时,似乎我必须包含对来自python-dev 包的python 头文件的引用。为了清楚起见,我实际上并没有通过这样做来包括口译员,对吗?

以下是我在搜索过程中发现的一些资源,证明可以将简单的 python 脚本编译为可执行的编译 C 程序:Compile main Python program using Cython http://masnun.rocks/2016/10/01/creating-an-executable-file-using-cython/

这是一些相关的 cython 文档: http://cython.readthedocs.io/en/latest/src/reference/compilation.html

【问题讨论】:

  • 一般这种方式对外保存对象是为了持久化数据;您通常无法从对象中序列化逻辑。您可以将数据导入其他进程,然后使用该程序中定义的方法将其反序列化回类/对象中。
  • @EliKorvigo,那 Py_Initialize() 东西实际上不是调用了 python 运行时环境吗?我想避免这种情况,因为我的嵌入式应用程序没有 python。
  • @Joe 是的,pickle 通常用于从 python 中保存并读回 python。这里的想法是我会编译一些封装逻辑的东西,而泡菜封装对象设置,部分逻辑是在启动时读取设置。
  • 如果不链接到 Python 解释器,您将无法做到这一点。

标签: python c compilation cython


【解决方案1】:

恐怕这个答案只是解释了为什么我不认为你想要的东西是现实的,而不是提供解决方案。值得看看 Cython 为稍作修改的 compileme.pyx 生成的代码

cdef public get_unpickled():
    import pickle
    return pickle.load(open('thing.pkl', 'r'))

这将创建一个您可以愉快地从 C 调用的函数(签名在 compileme.h 中生成,并且是 __PYX_EXTERN_C PyObject *get_unpickled(void);)。生成的包含实现的“.c”文件很长,但相关部分如下所示:

__pyx_t_1 = __Pyx_Import(__pyx_n_s_pickle, 0, -1);
__pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_v_pickle, __pyx_n_s_load);
__pyx_t_3 = __Pyx_PyObject_Call(__pyx_builtin_open, __pyx_tuple_, NULL);
__pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_t_3);

为了清楚起见,我已经把它减少了很多(主要是删除引用计数和一些检查),但你可以看到它使用 Python 导入机制从 Python 标准库加载 pickle 模块,它确实是 @987654328 @ 获取函数load。它调用 Python 内置函数 open,然后调用 pickle.load。所有这些操作都需要libpython。

然后我们考虑what pickle does - 它基本上获取您的类来自的 .py 文件,导入该文件,并创建您的类的新实例,然后用文件中的数据填充实例字典(可能调用一些特殊方法如果存在)。同样,这完全取决于使用 Python。

最后让我们考虑一下你可以用get_unpickled 的结果做什么。你有一个PyObject*,一个相当不透明的 C 结构。它的大部分信息可能存储在其内部的 Python 字典中,您可以通过 Python C API PyObject_GetAttrString 和相关函数访问该字典。但是,此数据仍存储为其他 PyObjects,您需要使用 Python C API 访问这些数据。 (如果它是 Cython 类,则数据可能存储在更易于访问的 C 结构字段中,这些字段需要较少使用 libpython,但可能不是没有)。


总之,Cython 主要是使用 Python C API 实现的,除了绝对最琐碎的程序之外,它需要访问 libpython 库。使用 pickle 等 Python 标准库函数也需要安装 Python 标准库。因此,如果不需要将 Python 与 C 程序捆绑在一起,就无法真正实现这一点。您链接的示例属于此类 - 它们是 C 程序,但它们依赖于存在的 Python。


更好的解决方案可能是查看 Python 和 C 都支持的常见序列化格式,例如 JSON、XML 或 HDF5,以允许您以一种语言保存数据并以另一种语言检索数据,而无需花费太多精力可能。

【讨论】:

  • 即使是一个明确的解释为什么不也是有用的,所以我可以知道放弃尝试。
  • 捆绑python有多大? libpython 不是一个成熟的解释器,所以这些编译版本应该仍然更轻更快,对吧?只是不像在 C 中实际重新实现事物那样轻巧快速。
  • 在我的 PC 上它大约是 3MB(Python 可执行文件实际上只有大约 10kb - 实际上几乎所有细节都在 libpython 中)。您还需要一些(但可能不是全部)Python 标准库。总共大约 30MB(但您也许可以选择您需要的部分)。
  • 我认为您可以使用 PyInstaller(或类似的)之类的工具来仅选择您使用的库,但我自己从未使用过它,因此无法提供真正的帮助
猜你喜欢
  • 1970-01-01
  • 2021-01-30
  • 1970-01-01
  • 2018-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 1970-01-01
相关资源
最近更新 更多