在Cython中创建可执行文件

59 投票
4 回答
60931 浏览
提问于 2025-04-17 22:58

我最近在玩Cython。平时我用Python编程,但之前学过C语言。现在我想弄一个可以独立运行的程序,但不知道怎么做。

我已经下载了Cython,并且可以创建一个.pyx文件(这其实就是一个普通的Python文件,只是后缀名是.pyx),然后我可以在Python的命令行里运行它,方法是使用:

import pyximport; pyximport.install()

我可以在命令行用这个命令生成一个.c文件:cython file.pyx。然后我可以通过建立一个标准的setup.py文件来生成一个.so文件,并执行:

setup.py build_ext --inplace

我尝试用gcc把.so文件做成可执行文件,试了很多选项,但总是缺少很多文件、头文件等等。我试着从各个地方指向这些头文件,但都没有成功。而且我对gcc的选项也不太了解,甚至不知道我是不是应该用gcc。

我现在有点困惑,因为我可以在Python命令行里运行我的程序,但在命令行下却不行(我不想让用户进入命令行,去导入模块什么的)。

我到底漏掉了什么呢?

4 个回答

3

基于Rafael的回答,这里有一个关于如何用Cython和Python 3制作一个“Hello, World”可执行文件的简单说明(在Termux上测试过),接下来是Python 2的说明:

我的系统上似乎没有cython3这个选项,所以我用别的方法:

文件叫做test.py:

print("Hello, world!")

然后这样做:

cython -3 --embed -o test.c test.py

接下来你需要弄清楚你需要什么包含路径,也许还需要一些库。

我使用的是Python 3.10.6,所以我用一个叫做python3.10-config的程序来获取信息,然后再输入到gcc中:

python3.10-config --includes

对我来说,输出是这样的:

-I/data/data/com.termux/files/usr/include/python3.10 -I/data/data/com.termux/files/usr/include/python3.10

它给了我两次相同的路径(我不知道为什么),所以只用那个路径就行了(我觉得只需要用一次)。

然后做:

python3.10-config --libs

对我来说,输出是这样的:

-lpython3.10 -lcrypt -ldl  -lm -lm

我不确定每一个都对每个程序都重要,因为“Hello, world”这个程序只用-lpython3.10就能工作。

无论如何,然后我这样做,它给了我一个可执行文件:

gcc -Os -I/data/data/com.termux/files/usr/include/python3.10 test.c -lpython3.10 -o test

还有一个叫做python2.7-config的程序。

如果我想用Python 2.7.18制作一个可执行文件,我可以这样做,而不是上面的步骤:

cython -2 --embed -o test.c test.py

然后

python2.7-config --includes
python2.7-config --libs

这给了我这些:

  • -I/data/data/com.termux/files/usr/include/python2.7 -I/data/data/com.termux/files/usr/include/python2.7
  • -lpython2.7 -ldl -lm

最后,我这样做就能得到可执行文件:

gcc -Os -I/data/data/com.termux/files/usr/include/python2.7 test.c -lpython2.7 -o test
8

这是一个关于Windows和MS Visual Studio 14的解决方案(因为没有人提到过cl.exe的参数)。

首先,使用embed参数来生成test.c文件:

cython test.pyx --embed

然后编译它:

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
cl test.c /I C:\Python37\include /link C:\Python37\libs\python37.lib

输出会是一个小的可执行文件test.exe(在我的例子中,运行print("Hello World")生成的文件大小是140 KB)。

注意事项:

24

在Ubuntu上测试过这个:

使用以下方法安装 Cython(适用于Python 2):

sudo apt-get install cython

如果你用的是Python 3:

sudo apt-get install cython3

要把Python代码编译成C代码,运行这个命令(如果是Python 3,就把 cython 改成 cython3):

cython --embed -o example.c example.py

这会生成一个 example.c 文件。

接下来编译这个 example.c 文件:

gcc -Os -I /usr/include/python2.7 example.c -lpython2.7 -o example

运行这个文件:

./example

对于Python 3,类似这样的命令应该可以工作(不过没测试过):

gcc -Os -I /usr/include/python3.6 example.c -lpython3.6 -o example

其中 python3.x 是你电脑上安装的Python版本。

74

你需要的是Cython编译器的--embed选项。虽然关于这个选项的文档不多,但我找到了一些信息,这里有个简单的示例可以参考。

要把Cython源代码编译成可以生成可执行文件的C文件,你可以使用类似cython myfile.pyx --embed的命令,然后用你选择的C编译器进行编译。

在编译C源代码时,你还需要包含Python头文件所在的目录,并链接到你系统上对应的Python共享库(如果你使用的是Python 2.7,文件名可能是libpython27.solibpython27.a)。

编辑:这里有一些关于如何获取包含正确头文件和链接正确库的命令的更多说明。

正如我之前提到的,你需要这样运行Cython编译器:

cython <cython_file> --embed

如果使用gcc编译,你需要找到你系统上Python头文件的位置(可以通过运行distutils.sysconfig.get_python_inc()来获取这个位置,记得先导入它)。这个位置通常是在你的Python安装目录下的/include子目录。

你还需要找到Python共享库。对于Python 2.7,Windows上是libpython27.a,而Linux上是libpython2.7.so

然后你的gcc命令将是

gcc <C_file_from_cython> -I<include_directory> -L<directory_containing_libpython> -l<name_of_libpython_without_lib_on_the_front> -o <output_file_name>

建议你加上-fPIC选项。在64位Windows机器上,你还需要加上-D MS_WIN64选项,这样mingw就知道要为64位Windows编译。

如果你编译的内容依赖于NumPy,你还需要包含NumPy头文件所在的目录。你可以通过运行numpy.get_include()来找到这个文件夹(同样,记得先导入numpy)。然后你的gcc命令就变成了

gcc <C_file_from_cython> -I<include_directory> -I<numpy_include_directory> -L<directory_containing_libpython> -l<name_of_libpython_without_lib_on_the_front> -o <output_file_name>

这个gcc命令选项的指南可能会对你有帮助。

另外,如果可能的话,我建议你使用Cython的内存视图。这样你就不需要包含NumPy的头文件,也不需要在Cython文件中包含NumPy的pxd文件。这也会让C编译器在优化切片操作时更容易。

撰写回答