Din*_*uja 5 python cython cythonize
我正在尝试从 python 源文件创建一个 unix 可执行文件。
我有两个文件,p1.py并且p2.py
p1.py:-
from p2 import test_func 
print (test_func())
Run Code Online (Sandbox Code Playgroud)
p2.py:-
def test_func():
    return ('Test')
Run Code Online (Sandbox Code Playgroud)
现在,正如我们所看到的,p1.py依赖于p2.py. 我想通过将两个文件组合在一起来制作一个可执行文件。我正在使用 cython。
我将文件名分别更改为p1.pyx和p2.pyx。
现在,我可以使用 cython 使文件可执行,
cython p1.pyx --embed
Run Code Online (Sandbox Code Playgroud)
它将生成一个名为 .c 的 C 源文件p1.c。接下来我们可以使用 gcc 使其可执行,
gcc -Os -I /usr/include/python3.5m -o test p1.c -lpython3.5m -lpthread -lm -lutil -ldl 
Run Code Online (Sandbox Code Playgroud)
但是如何将两个文件合并为一个可执行文件呢?
您必须跳过一些循环才能使其正常工作。
首先,您必须意识到生成的可执行文件是一个非常薄的层,它只是将整个工作委托给(即从中调用函数)pythonX.Ym.so。调用时可以看到这种依赖关系
ldd test
...
libpythonX.Ym.so.1.0 => not found
...
Run Code Online (Sandbox Code Playgroud)
因此,要运行该程序,您需要显示LD_LIBRARY_PATH该程序的位置libpythonX.Ym.so或使用选项构建 exe --rpath,否则在动态加载程序启动时test将抛出类似于以下内容的错误
/test:加载共享库时出错:libpythonX.Ym.so.1.0:无法打开共享对象文件:没有这样的文件或目录
通用构建命令如下所示:
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>
Run Code Online (Sandbox Code Playgroud)
还可以针对 python 库的静态版本进行构建,从而消除对 libpythonX.Ym 的运行时依赖,例如参见此SO-post。
生成的可执行文件的test行为与 python 解释器完全相同。这意味着现在test将会失败,因为它找不到该模块p2。
一种简单的解决方案是对 p2 模块进行 cythonize 就地 ( 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 ...
Run Code Online (Sandbox Code Playgroud)
但现在出现了一个新的(或旧的)问题:生成的test可执行文件无法再次找到 module ,因为python 路径上p2没有p2.pyno 。p2.so
关于这个问题有两个类似的SO问题,这里和这里。在您的情况下,建议的解决方案有点矫枉过正,在这里,在将 p2 模块导入到 -file 中之前对其进行初始化就足够了p1.pyx:
# 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())
Run Code Online (Sandbox Code Playgroud)
如果模块之间存在循环依赖关系,则在导入模块之前调用模块的 init 函数(实际上该模块不会真正再次导入,只会在缓存中查找)也可以工作。例如,如果 modulep2导入 module p3,则 module 会p2依次导入。
警告:从 Cython 0.29 开始,Cython 对于 Python>=3.5 默认使用多阶段初始化,因此调用PyInit_p2是不够的(例如参见此 SO-post)。要关闭此多阶段初始化,-DCYTHON_PEP489_MULTI_PHASE_INIT=0应将其传递给 gcc 或类似于其他编译器。
注意:然而,即使在完成上述所有操作之后,嵌入式解释器也将需要其标准库(例如,参见此SO-post) - 要使其真正独立,还有很多工作要做!所以也许人们应该听听@DavidW的建议:
“不要这样做”可能是绝大多数人的最佳解决方案。
警告:如果我们声明PyInit_p2()为
from cpython cimport PyObject
cdef extern  PyObject *PyInit_p2();
PyInit_p2(); # TODO: error handling if NULL is returned
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
为 -version 生成object,生成的代码变为:
(void)(PyInit_p2());
Run Code Online (Sandbox Code Playgroud)
即没有错误检查!
另一方面使用
cdef extern from *:
    """
    PyObject *PyInit_p2(void);
    """
    object PyInit_p2()
Run Code Online (Sandbox Code Playgroud)
不适用于 g++ - 必须添加extern C到声明中。
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           3878 次  |  
        
|   最近记录:  |