gpt4 book ai didi

python - 使用 cython 从多个 pyx 文件生成可执行文件

转载 作者:行者123 更新时间:2023-11-30 22:05:36 26 4
gpt4 key购买 nike

我正在尝试从我的 python 源文件创建一个 unix 可执行文件。

我有两个文件,p1.pyp2.py

p1.py:-

from p2 import test_func 
print (test_func())

p2.py:-

def test_func():
return ('Test')

现在,我们可以看到 p1.py 依赖于 p2.py 。我想通过将两个文件组合在一起来制作一个可执行文件。我正在使用 cython。

我将文件名分别更改为 p1.pyxp2.pyx

现在,我可以使用 cython 使文件可执行,

cython p1.pyx --embed

它将生成一个名为 p1.c 的 C 源文件。接下来我们可以使用gcc使其可执行,

gcc -Os -I /usr/include/python3.5m -o test p1.c -lpython3.5m -lpthread -lm -lutil -ldl 

但是如何将两个文件合并为一个可执行文件?

最佳答案

您必须跳过一些循环才能使其正常工作。

首先,您必须意识到生成的可执行文件是一个非常薄的层,它只是将整个工作委托(delegate)给(即从中调用函数)pythonX.Ym.so。您可以在调用时看到这种依赖关系

ldd test
...
libpythonX.Ym.so.1.0 => not found
...

因此,要运行该程序,您需要将 LD_LIBRARY_PATH 显示为 libpythonX.Ym.so 的位置,或者使用 - 构建 exe -rpath 选项,否则在 test 动态加载器启动时会抛出类似于

的错误

/test: error while loading shared libraries: libpythonX.Ym.so.1.0: cannot open shared object file: No such file or directory

通用构建命令如下所示:

gcc -fPIC <other flags> -o test p1.c -I<path_python_include> -L<path_python_lib> -Wl,-rpath=<path_python_lib> -lpython3.6m <other_needed_libs>

还可以针对 python 库的静态版本进行构建,从而消除对 libpythonX.Ym 的运行时依赖,例如参见此 SO-post .

<小时/>

生成的可执行文件test的行为与Python解释器完全相同。这意味着现在 test 将失败,因为它找不到模块 p2

一个简单的解决方案是就地 cythonize p2 模块(cythonize p2.pyx -i):您将获得所需的行为 - 但是,您必须分发生成的共享对象p2.so 以及 test

将两个扩展捆绑到一个可执行文件中很容易 - 只需将两个 cythonized c 文件传递​​给 gcc:

# creates p1.c:
cython --empbed p1.pyx
# creates p2.c:
cython p2.pyx
gcc ... -o test p1.c p2.c ...

但是现在出现了一个新的(或旧的)问题:生成的 test 可执行文件无法再次找到模块 p2,因为没有 p2。 py 并且 python 路径上没有 p2.so

关于这个问题有两个类似的SO问题,herehere 。在您的情况下,建议的解决方案有点矫枉过正,在这里,在将 p2 模块导入到 p1.pyx 文件中使其工作之前初始化 p2 模块就足够了:

# making init-function from other modules accessible:
cdef extern object PyInit_p2();

#init/load p2-module manually
PyInit_p2() #Cython handles error, i.e. if NULL returned

# actually using already cached imported module
# no search in python path needed
from p2 import test_func
print(test_func())

如果模块之间存在循环依赖关系,在导入模块之前调用模块的 init 函数(实际上该模块不会真正被导入第二次,只会在缓存中查找)也可以工作。例如,如果模块 p2 导入模块 p3,模块又导入 p2

<小时/>

警告:自 Cython 0.29 起,Cython 对于 Python>=3.5 默认使用多阶段初始化,因此调用 PyInit_p2 是不够的(参见例如 this SO-post ) 。要关闭此多阶段初始化,应将 -DCYTHON_PEP489_MULTI_PHASE_INIT=0 传递给 gcc 或类似于其他编译器。

<小时/>

注意:但是,即使完成上述所有操作,嵌入式解释器仍将需要其标准库(例如,参见此 SO-post ) - 还有更多努力使其真正独立!所以也许有人应该注意@DavidW's advice :

"don't do this" is probably the best solution for the vast majority of people.

<小时/>

警告:如果我们将 PyInit_p2() 声明为

from cpython cimport PyObject
cdef extern PyObject *PyInit_p2();

PyInit_p2(); # TODO: error handling if NULL is returned

Cython 将不再处理错误,这是我们的责任。而不是

PyObject *__pyx_t_1 = NULL;
__pyx_t_1 = PyInit_p2(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 4, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

object版本生成,生成的代码变成:

(void)(PyInit_p2());

即没有错误检查!

另一方面使用

cdef extern from *:
"""
PyObject *PyInit_p2(void);
"""
object PyInit_p2()

不适用于 g++ - 必须在声明中添加 extern C

关于python - 使用 cython 从多个 pyx 文件生成可执行文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52959902/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com