Cython 是一个将类似 Python 的代码文件编译为 C 代码的编译器。尽管如此,“Cython 不是 Python 到 C 的翻译器”。也就是说,它不会将完整的程序"转换为 C"------相反,结果会充分利用 Python 运行时环境。一种看待它的方式可能是,您的代码仍然是 Python,因为它在 Python 运行时环境中运行,但不是编译为解释型 Python 字节码,而是编译为本机机器代码(但添加了额外的语法,以便轻松嵌入更快的类 C 代码)。
这有两个重要的后果:
速度。不过,多少很大程度上取决于所涉及的计划。典型的 Python 数值程序往往获得的收益很少,因为大部分时间都花在以高级方式使用的较低级别 C 上。然而,当添加键入信息时,for 循环式程序可以获得许多数量级(并且因此成为现实的替代方案)。
轻松调用 C 代码。 Cython 的目的之一是允许轻松包装 C 库。在 Cython 中编写代码时,您可以像调用 Python 代码一样轻松地调用 C 代码。
目前尚不支持极少数 Python 结构,尽管我们的既定目标是让 Cython 编译所有 Python 代码,但您可以看到与 Python 的局限性差异。
使用 Cython 包括以下步骤:
编写 .pyx
源文件
运行Cython编译器生成C文件
运行C编译器生成编译库
运行Python解释器并要求它导入模块
但是,有多种选项可以自动执行这些步骤:
SAGE 数学软件系统为从交互式命令行或通过笔记本界面(如 Maple/Mathematica)使用 Cython 和 NumPy 提供了出色的支持。请参阅此文档。
Cython 可以用作 Jupyter Notebook 中的扩展,只需在单元格顶部添加 %%cython
即可轻松编译和使用 Cython 代码。有关更多信息,请参阅使用 Jupyter Notebook。
Cython 附带了 pyximport 的一个版本,以便您可以将 pyx 文件动态导入到 Python 中并自动编译它们(请参阅使用 pyximport 进行编译)。
Cython 支持 setuptools,因此您可以非常轻松地创建自动执行该过程的构建脚本,这是 Cython 实现的库和包的首选方法。请参阅基本 setup.py。
手动编译(见下文)
注意 如果使用 SAGE 之外的其他交互式命令行环境(例如 IPython 或 Python 本身),则在重新编译模块时重新启动该进程非常重要。仅仅再次发出"进口"声明是不够的。
如果您已经有 C 编译器,只需执行以下操作:
pip install Cython
否则,请参阅安装页面。
截至撰写本文时,SAGE 附带的 Cython 版本比本教程所需的版本更旧。因此,如果使用 SAGE,您应该下载最新的 Cython,然后执行:
$ cd path/to/cython-distro
$ path-to-sage/sage -python setup.py install
这会将最新的 Cython 安装到 SAGE 中。
由于了解正在发生的情况始终很重要,因此我将在这里描述手动方法。第一个 Cython 运行:
$ cython yourmod.pyx
这将创建 yourmod.c
,它是 Python 扩展模块的 C 源代码。一个有用的附加开关是 -a
,它将生成一个文档 yourmod.html
),该文档显示哪个 Cython 代码逐行转换为哪个 C 代码。
然后我们编译C文件。这可能会根据您的系统而有所不同,但 C 文件应该像构建 Python 一样构建。用于编写扩展的 Python 文档应该有一些详细信息。在 Linux 上,这通常意味着:
$ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o yourmod.so yourmod.c
gcc
应该有权访问 NumPy C 头文件,因此如果它们未安装在 /usr/include/numpy
或类似位置,您可能需要为这些文件传递另一个选项。如果您编写以下内容,则只需提供 NumPy 标头:
cimport numpy
在你的 Cython 代码中。
这会在同一目录中创建 yourmod.so
,Python 可通过使用普通的 importyourmod
语句导入该目录。
setuptools 允许我们创建 setup.py 文件来自动编译 Cython 文件和生成的 C 文件:
from setuptools import Extension, setup
from Cython.Build import cythonize
import numpy
extensions = [
Extension("*", ["*.pyx"],
include_dirs=[numpy.get_include()]),
]
setup(
name="My hello app",
ext_modules=cythonize(extensions),
)
NumPy 标头的路径通过 include_dirs=[numpy.get_include()]
参数传递给 C 编译器。
注意 使用内存视图或使用 importnumpy
导入 NumPy 并不意味着您必须添加 NumPy 包含文件的路径。仅当使用 cimportnumpy
时才需要添加此路径。
尽管如此,您仍然可能会从编译器收到如下警告,因为 Cython 不会禁用旧的已弃用的 Numpy API 的使用:
.../include/numpy/npy_1_7_deprecated_api.h:15:2: warning: #warning "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
在 Cython 3.0 中,您可以通过在构建中将 C 宏 NPY_NO_DEPRECATED_API
定义为 NPY_1_7_API_VERSION
来消除此警告,例如:
# distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION
或(见下文):
Extension(
...,
define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")],
)
对于较旧的 Cython 版本,设置此宏将使 C 编译失败,因为 Cython 生成使用此已弃用的 C-API 的代码。然而,即使在最近的 NumPy 版本中,该警告也没有负面影响。您可以忽略它,直到您(或您的库的用户)切换到更新的 NumPy 版本,该版本删除了这个长期弃用的 API,在这种情况下,您还需要使用 Cython 3.0 或更高版本。因此,越早切换到 Cython 3.0,对用户来说就越好。
未完待续…