小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
软硬件环境
- ubuntu 18.04 64bit
- anaconda with python 3.6
- cython 0.27.3
cython简介
Cython 是让 Python 脚本支持 C 语言扩展的编译器,是 python 的超集,Cython 能够将 Python 和 C 混合编码的 .pyx 脚本转换为 C 代码,主要用于优化 Python 脚本性能或 Python 调用 C 函数库。由于 Python 固有的性能差的问题,用 C 扩展 Python 成为提高 Python 性能常用方法。本文主要是介绍如何使用 cython 将 python 代码编译成 C 语言中的动态链接库,也就是常说的 so。
cython安装
首先使用 pip 进行安装
pip install cython
准备python模块
我们在 anaconda 的默认库安装目录
cd /home/xugaoxiang/anaconda3/lib/python3.6/site-packages
mkdir djstava
cd djstava
touch __init__.py test.py
创建文件 __init__.py 和 test.py
编辑 test.py 文件内容为
def test():
'''
Just an example.
'''
print('Hello Cython.')
接下来测试下创建的模块是否可以被导入,其中的方法是否可以正常运行。这个可以用 python 或者 ipython 来进行
xugaoxiang@ubuntu:~/anaconda3/lib/python3.6/site-packages/djstava$ python
Python 3.6.4 |Anaconda custom (64-bit)| (default, Jan 16 2018, 18:10:19)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import djstava.test
>>> djstava.test.test
<function test at 0x7ff35f135598>
>>> djstava.test.test()
Hello Cython.
OK,至此已经创建好 test 模块
编译成so库
接下来利用 cython 将 test.py 转换成 test.c,然后编译成动态链接库 so
cd /home/xugaoxiang/anaconda3/lib/python3.6/site-packages/djstava
cython test.py
生成了 test.c 文件,然后使用如下命令
gcc -c -fPIC -I/home/xugaoxiang/anaconda3/include/python3.6m test.c
生成目标文件即 .o 文件,最后链接成动态库
gcc -shared test.o -o test.so
到此,动态库正式生成。
测试so
这次我们用 ipython 来测试,首先进到目录 /home/xugaoxiang/anaconda3/lib/python3.6/site-packages/djstava,将 test.py 文件删除,以免形成干扰
xugaoxiang@ubuntu:~/anaconda3/lib/python3.6/site-packages/djstava$ ipython
Python 3.6.4 |Anaconda custom (64-bit)| (default, Jan 16 2018, 18:10:19)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import djstava.test
In [2]: djstava.test.test
Out[2]: <cyfunction test at 0x7f90945bbc80>
In [3]: djstava.test.test()
Hello Cython.
调用 test 模块的方法和执行的结果与 test.py 一模一样
另外,关于在 python 中如何去调用 C 语言动态链接库,可以参考之前的文章 xugaoxiang.com/2019/12/08/…
结合setup.py使用
如果还不了解 setup.py 的话,请移步 xugaoxiang.com/2019/12/08/…。为了不跟上面的工程相冲突,我们新建一个新的目录 /home/xugaoxiang/test,然后创建文件 test.pyx,pyx 文件允许 C 和 python 进行混编,它的内容如下
def test():
'''
Just an example.
'''
print('Hello Cython.')
编写 setup.py 文件
from distutils.core import setup
from Cython.Build import cythonize
setup(
name='Test pyx',
ext_modules=cythonize('test.pyx')
)
编辑完毕,使用命令 python setup.py build 开始安装
xugaoxiang@ubuntu:~/test$ python setup.py build
Compiling test.pyx because it changed.
[1/1] Cythonizing test.pyx
running build
running build_ext
building 'test' extension
creating build
creating build/temp.linux-x86_64-3.6
gcc -pthread -B /home/xugaoxiang/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/xugaoxiang/anaconda3/include/python3.6m -c test.c -o build/temp.linux-x86_64-3.6/test.o
creating build/lib.linux-x86_64-3.6
gcc -pthread -shared -B /home/xugaoxiang/anaconda3/compiler_compat -L/home/xugaoxiang/anaconda3/lib -Wl,-rpath=/home/xugaoxiang/anaconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-3.6/test.o -o build/lib.linux-x86_64-3.6/test.cpython-36m-x86_64-linux-gnu.so
编译后的文件目录结构如下
xugaoxiang@ubuntu:~/test$ ls -R
.:
build setup.py test.c test.pyx
./build:
lib.linux-x86_64-3.6 temp.linux-x86_64-3.6
./build/lib.linux-x86_64-3.6:
test.cpython-36m-x86_64-linux-gnu.so
./build/temp.linux-x86_64-3.6:
test.o
注意到,test.c 文件已经生成,同时还有 build 下的 .o 及 .so 文件。同样的,我们用 ipython 来测试下生成的 so 文件,在当前目录下创建一个全新的文件夹,然后将上步中生成的 so 文件拷贝过来,再新建一个 __init__.py 文件,内容是
from test import *
然后开始测试
xugaoxiang@ubuntu:~/test/test$ ipython
Python 3.6.4 |Anaconda custom (64-bit)| (default, Jan 16 2018, 18:10:19)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import test
In [2]: test.test()
Hello Cython.