Cython
编译运行
- Use
setup.py
file
from setuptools import setup
from Cython.Build import cythonize
from Cython.Compiler import Options
Options.docstring = False
setup(
name='Hello world app',
ext_modules=cythonize("hello.pyx", compiler_directives={'embedsignature': True}), # target .pyx file and compile options
zip_safe=False,
)
如果是要传入多个文件,可以使用正则表达式匹配,eg.
*.pyx
。或者使用一个字符串的列表包含。
编译:
python setup.py build_ext --inplace
-
Use pyximport
-
Use Jupyter Notebook
%load_ext Cython
# Start a new cell
%%cython
cdef int a = 0
for i in range(10):
a += i
print(a)
编译选项
编译选项如上 setup.py
中所示,使用 Options
设置,参考
compiler derctives
可以在 setup.py
中设置,或者是在 .pyx
文件顶部加入如下注释
#cython: language_level=3, ...
变量类型
声明变量类型,可以声明几乎所有的 C 类型(数,指针,结构体,联合体)
cdef int i # declare an integer
函数类型声明
cdef int func(int a, int b)
cpdef int func(int a, int b)
cdef
只能在 .pyx
内调用,无法向 Python 提供接口。cpdef
可以向 Python 提供接口。
- C array
cdef int p[1000]
for i in p[:100]:
if i % 10 == 0:
a += i
从官方给出样例中可以看出,数组是可以用 Python 切片并且循环的。与 C 相同,函数中声明的变量放在堆栈(stack)中,所以不要声明过大的数组。关于堆栈和堆,参考
- string
返回字符串
from libc.stdlib cimport malloc
from libc.string cimport strcpy, strlen
cdef char* hello_world = 'hello world'
cdef Py_ssize_t n = strlen(hello_world)
cdef char* c_call_returning_a_c_string():
cdef char* c_string = <char *> malloc((n + 1) * sizeof(char))
if not c_string:
raise MemoryError()
strcpy(c_string, hello_world)
return c_string
不要在函数里面声明一个字符数组然后返回。因为这个字符数组是在函数栈里面,函数返回后就释放了。应该使用 malloc
动态分配空间后返回!!!
函数调用
通过头文件和链接库就可以随意调用 C 函数
# Python 可以直接调用
cdef extern from "add.h":
cpdef int func(int, int)
# Python 不可直接调用
cdef extern from "add.h":
int func(int, int)
在 setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize
ext_modules = [
Extension("space",
sources=["space.pyx"],
include_dis = ['./']
libraries=["add"], # Unix-like specific
library_dirs = ['./']
),
]
setup(
name='Hello world app',
ext_modules=cythonize(ext_modules),
zip_safe=False,
)
将之前的 C 函数编译成动态链接库。然后在
.pyx
中导入头文件并声明函数。之后再setup.py
中编译链接,就可以调用 C 函数。编译时Extension
参数见此处