根据documentation,可以使用从Cython生成的C头文件。我已经按照Hello World
示例没有问题,现在我想尝试不同的东西。我想使用公共声明来使用自定义方法。我的代码结构如下:
hello.pyx
cdef public void say_hello():
print("Hello World")
setup.py
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
)
的main.c
#include "hello.h"
int main(void){
say_hello();
}
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
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 */
据我了解,似乎gcc无法获得正确版本的Python(我使用的是Python 3.5)。有办法以某种方式设置吗?此外,如果情况确实如此,那么为什么在运行python3 setup.py build_ext
命令时,链接没有处理?
我没有多少C经验,所以我可能会遗漏一些东西。
答案 0 :(得分:4)
我认为问题是你的误解,distutils
将为你构建可执行文件。事实并非如此。
目标是使用C程序中的一些python功能。让我们自己做一些必要的步骤:
hello.h
和hello.c
。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
现在我们的工作目录中有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!
}
我不确定为什么cython的结果不包含Python.h
(这会使它自包含),但这就是它的样子 - 你需要在{{{{{{ 1}}。在使用hello.h
功能之前和之后,也应调用Py_Initialize()
和Py_Finalize()
。此外,您需要使用hello.h
初始化模块hello
,否则在可执行文件的开头,分段错误会困扰你。
现在我们需要找到用于编译的标志。这可以使用实用程序inithello()
(我使用python2.7,你需要对python3执行相同操作)和选项/usr/bin/python-config
:
--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
现在我们的工作目录中有两个目标文件>>>gcc -c hello.c -o hello.o <our cflags>
>>>gcc -c main.c -o main.o <our cflags>
和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
现在我们终于可执行了!
实际上,它并不是所要求的,但是通过使用选项>>> gcc main.o hello.o -o prog <our ldflags>
,还有另一种可能性从cython代码生成可执行文件。通过此开关,不仅可以对模块进行cython化,还可以创建主要功能。为此,您的--embed
可能如下所示:
hello.pyx
也可以使用#hello.pyx:
cdef public void say_hello():
print("Hello World")
##main:
say_hello()
技巧,但不需要。
现在运行后:
__name__=="__main__"
在生成的>>>python -m cython hello.pyx --embed
中创建main
函数,负责设置/初始化python环境。所以我们可以构建并链接它:
hello.c
我们已经完成了 - 不需要知道,如何初始化整个python-stuff!
答案 1 :(得分:0)
您可以使用api
关键字
cdef api void hello():
print("hello")
return;
这将在编译时自动生成一个“ hello.h”。