初识Cython扩展

Cython是什么?

Cython是一种扩展Python的语言,提供了Python和C的综合功能,支持在C/C++代码中调用,通过支持给函数、变量和类声明类型来实现,这些类型声明让Cython能够将Python脚本调整为纯C性能。

The Cython language is a superset of the Python language that additionally supports calling C functions and declaring C types on variables and class attributes. This allows the compiler to generate very efficient C code from Cython code.

学习过程中使用的资料为《Python高性能》一书和官方文档: http://docs.cython.org/en/latest/#

安装Cython

我使用环境是Ubuntu 18.04、Python 3.7.3

1
$ pip install Cython

编译Cython扩展

新建一个hello.pyx的文件:

1
2
3
4
5
(Dev) $ cat hello.pyx
#!python
#cython: language_level=3
def hello():
print('Hello, World!')

注意加上#cython: language_level=3这一行,否则会报出警告:

1
FutureWarning: Cython directive 'language_level' not set, using 2 for now (Py2). This will change in a later release!

使用Cython命令读取hello.pyx并生成hello.c文件:

1
(Dev) $ cython hello.pyx

使用命令行GCC

接下来使用GCC编译器将hello.c文件编译为Python的扩展模块,需要启动Python专门的编译选项:

1
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -lm -I /usr/local/python3/include/python3.7m -o hello.so hello.c

关于其中的编译选项,可以查看相关的文档,fno-strict-aliasing这个倒是第一次遇到,要研究一下:

此时查看目录结构,可以看到已经生成了hello.so文件:

1
2
3
4
5
6
7
$ tree -h
.
├── [100K] hello.c
├── [ 75] hello.pyx
└── [ 25K] hello.so

0 directories, 3 files

该扩展模块可以直接在Python会话中导入使用:

1
2
3
4
5
6
7
$ python
Python 3.7.3 (default, May 20 2019, 22:45:47)
[GCC 7.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>> hello.hello()
Hello, World!

使用distutils

distutils是标准的Python打包工具,可以更简单的编译Cython代码。在目录下新建一个简单的setup.py脚本:

1
2
3
4
5
6
7
(Dev) $ cat setup.py 
from distutils.core import setup
from Cython.Build import cythonize

setup(name='Hello',
ext_modules = cythonize('hello.pyx')
)

cythonize可接受一个字符串或字符串列表,其中包含要编译的Cython模块。

执行setup.py脚本:

1
2
3
4
5
6
7
(Dev) $ python setup.py build_ext --inplace
running build_ext
building 'hello' extension
creating build
creating build/temp.linux-x86_64-3.7
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/home/top/myenv/Dev/include -I/usr/local/python3/include/python3.7m -c hello.c -o build/temp.linux-x86_64-3.7/hello.o
gcc -pthread -shared build/temp.linux-x86_64-3.7/hello.o -o /home/top/test/hello.cpython-37m-x86_64-linux-gnu.so

选项build_ext: 让脚本setup.py构建ext_modules中指定的扩展模块;
选项--inplace: 让这个脚本将输出文件hello.so放在源文件所在的目录。

此时的目录结构:

1
2
3
4
5
6
7
8
9
10
11
(Dev)$ tree -h
.
├── [4.0K] build
│   └── [4.0K] temp.linux-x86_64-3.7
│   └── [115K] hello.o
├── [100K] hello.c
├── [ 68K] hello.cpython-37m-x86_64-linux-gnu.so
├── [ 75] hello.pyx
└── [ 132] setup.py

2 directories, 5 files

在Python会话中,同样可以看到和命令行GCC模式一样的效果。

使用pyximport

可以使用pyximport进行自动编译Cython模块,只需要在脚本中加入pyximport.install(),在解释器中执行也是可以的:

1
2
3
4
5
6
>>> import pyximport
>>> pyximport.install()
(None, <pyximport.pyximport.PyxImporter object at 0x7fe2abddfeb8>)
>>> import hello
>>> hello.hello()
Hello, World!

但是该方法在同时涉及到C和Cython文件时就无法使用了。

使用Jupyter

Cython也可以被使用在Jupyter notebook中,首先安装Jupyter:

1
2
$ pip install jupyter
$ jupyter notebook

在Jupyter中加载Cython扩展:

1
In[1]:  %load_ext Cython

可以使用%%cython标记为单元格添加前缀以编译它:

1
2
3
4
5
In[2]:  %%cython
def hello():
print("Hello, World!")
In[3]: hello()
Hello, World!

几种编译Cython的方法就学习到这啦,接下来准备学习一下静态类型的使用😄

0%