根据文档,可以使用从 Cython 生成的 C 头文件。我已经Hello World毫无问题地遵循了这个例子,现在我想尝试一些不同的东西。我想使用公共声明来使用自定义方法。我的代码结构如下:
你好.pyx
cdef public void say_hello():
print("Hello World")
Run Code Online (Sandbox Code Playgroud)
设置文件
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [
Extension("hello", ["hello.pyx", "main.c"]),
]
setup(
name='Hello app',
cmdclass={'build_ext': build_ext},
ext_modules=ext_modules
)
Run Code Online (Sandbox Code Playgroud)
主文件
#include "hello.h"
int main(void){
say_hello();
}
Run Code Online (Sandbox Code Playgroud)
在main.c作为测试文件以验证该say_hello()方法按预期工作。构建安装文件python3 setup.py build_ext会产生以下输出。
running build_ext
skipping 'hello.c' Cython extension (up-to-date)
building 'hello' extension
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.5m -c hello.c -o build/temp.linux-x86_64-3.5/hello.o
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.5m -c main.c -o build/temp.linux-x86_64-3.5/main.o
In file included from main.c:1:0:
hello.h:26:1: error: unknown type name ‘PyMODINIT_FUNC’
PyMODINIT_FUNC inithello(void);
^
error:
command 'x86_64-linux-gnu-gcc' failed with exit status 1
Run Code Online (Sandbox Code Playgroud)
hello.h 文件包含以下内容
/* Generated by Cython 0.25.2 */
#ifndef __PYX_HAVE__hello
#define __PYX_HAVE__hello
#ifndef __PYX_HAVE_API__hello
#ifndef __PYX_EXTERN_C
#ifdef __cplusplus
#define __PYX_EXTERN_C extern "C"
#else
#define __PYX_EXTERN_C extern
#endif
#endif
#ifndef DL_IMPORT
#define DL_IMPORT(_T) _T
#endif
__PYX_EXTERN_C DL_IMPORT(void) say_hello(void);
#endif /* !__PYX_HAVE_API__hello */
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC inithello(void); // <-- Line 26
#else
PyMODINIT_FUNC PyInit_hello(void);
#endif
#endif /* !__PYX_HAVE__hello */
Run Code Online (Sandbox Code Playgroud)
据我了解,似乎 gcc 无法获得正确版本的 Python(我使用的是 Python 3.5)。有没有办法以某种方式设置它?另外,如果确实如此,为什么在我运行命令时没有处理这个链接python3 setup.py build_ext?
我对 C 没有太多经验,所以我可能会遗漏一些东西。
我认为问题在于您的误解,这distutils将为您构建可执行文件。事实并非如此。
Out 目标是使用 C 程序中的一些 Python 功能。让我们自己做必要的步骤:
hello.h和hello.c来自给定的 pyx 文件。hello.h来在 C 程序中导入 python 功能 ( main.c)。hello.c和main.c.第一步很简单:
#hello.pyx:
cdef public void say_hello():
print("Hello World")
>>>python -m cython hello.pyx
Run Code Online (Sandbox Code Playgroud)
现在我们有hello.c和hello.h在我们的工作目录中。你main.c错了,应该如下所示:
//main.c
#include <Python.h> //needed
#include "hello.h"
int main(void){
Py_Initialize(); //Needed!
inithello(); //Needed! called PyInit_hello() for Python3
say_hello();
Py_Finalize(); //Needed!
}
Run Code Online (Sandbox Code Playgroud)
重要提示:如果您使用 Python>=3.5 和 Cython>=0.29,您必须考虑多阶段模块初始化,如本SO-post 中所述- 否则生成的程序将崩溃。
我不确定为什么 cython 的结果不包括Python.h(这将使其独立),但它就是这样 - 您需要在hello.h. 此外Py_Initialize(),Py_Finalize()应该在使用hello.h.Also中的功能之前和之后调用。此外,您需要初始化模块hello,inithello()否则分段错误将在可执行文件的开头困扰您。
现在我们需要找到编译的标志。这可以通过实用程序/usr/bin/python-config(我使用 python2.7,你需要对 python3 执行相同的操作)和选项来完成--cflags:
>>> /usr/bin/python-config --cflags
-I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7
-fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong
-Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes
Run Code Online (Sandbox Code Playgroud)
所以让我们构建它:
>>>gcc -c hello.c -o hello.o <our cflags>
>>>gcc -c main.c -o main.o <our cflags>
Run Code Online (Sandbox Code Playgroud)
现在我们有目标文件hello.o和main.o我们的工作目录。我们需要链接它们,为了找到正确的标志,我们再次使用该python-config实用程序,但这次使用 - 选项--ldflags:
>>> /usr/bin/python-config --ldflags
--L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpython2.7
-lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
Run Code Online (Sandbox Code Playgroud)
这意味着:
>>> gcc main.o hello.o -o prog <our ldflags>
Run Code Online (Sandbox Code Playgroud)
现在我们终于有了我们的可执行文件!
实际上,这并不是所要求的,但是还有另一种可能性可以通过使用选项从 cython 代码生成可执行文件--embed。打开此开关后,不仅模块被 cythonized,而且主函数也被创建。为此,您hello.pyx可以如下所示:
#hello.pyx:
cdef public void say_hello():
print("Hello World")
##main:
say_hello()
Run Code Online (Sandbox Code Playgroud)
也可以使用__name__=="__main__"技巧,但不是必需的。
现在运行后:
>>>python -m cython hello.pyx --embed
Run Code Online (Sandbox Code Playgroud)
main结果hello.c中创建了一个函数,负责设置/初始化 python 环境。所以我们可以构建并链接它:
>>> gcc hello.c -o prog <our cflags> <our ldflags>
Run Code Online (Sandbox Code Playgroud)
我们已经完成了 - 不需要知道,如何正确地初始化整个 python 东西!