# Cython:Cython 语法,调用其他C库 ## Cython 语法 ### cdef 关键词 `cdef` 定义 `C` 类型变量。 可以定义局部变量: ```py def fib(int n): cdef int a,b,i ... ``` 定义函数返回值: ```py cdef float distance(float *x, float *y, int n): cdef: int i float d = 0.0 for i in range(n): d += (x[i] - y[i]) ** 2 return d ``` 定义函数: ```py cdef class Particle(object): cdef float psn[3], vel[3] cdef int id ``` 注意函数的参数不需要使用 cdef 的定义。 ### def, cdef, cpdef 函数 `Cython` 一共有三种定义方式,`def, cdef, cpdef` 三种: * `def` - Python, Cython 都可以调用 * `cdef` - 更快,只能 Cython 调用,可以使用指针 * `cpdef` - Python, Cython 都可以调用,不能使用指针 ### cimport In [1]: ```py from math import sin as pysin from numpy import sin as npsin ``` In [2]: ```py %load_ext Cython ``` 从标准 `C` 语言库中调用模块,`cimport` 只能在 `Cython` 中使用: In [3]: ```py %%cython from libc.math cimport sin from libc.stdlib cimport malloc, free ``` ### cimport 和 pxd 文件 如果想在多个文件中复用 `Cython` 代码,可以定义一个 `.pxd` 文件(相当于头文件 `.h`)定义方法,这个文件对应于一个 `.pyx` 文件(相当于源文件 `.c`),然后在其他的文件中使用 `cimport` 导入: `fib.pxd, fib.pyx` 文件存在,那么可以这样调用: ```py from fib cimport fib ``` 还可以调用 `C++` 标准库和 `Numpy C Api` 中的文件: ```py from libcpp.vector cimport vector cimport numpy as cnp ``` ## 调用其他C库 从标准库 `string.h` 中调用 `strlen`: In [4]: ```py %%file len_extern.pyx cdef extern from "string.h": int strlen(char *c) def get_len(char *message): return strlen(message) ``` ```py Writing len_extern.pyx ``` 不过 `Cython` 不会自动扫描导入的头文件,所以要使用的函数必须再声明一遍: In [5]: ```py %%file setup_len_extern.py from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( ext_modules=[ Extension("len_extern", ["len_extern.pyx"]) ], cmdclass = {'build_ext': build_ext} ) ``` ```py Writing setup_len_extern.py ``` 编译: In [6]: ```py !python setup_len_extern.py build_ext --inplace ``` ```py running build_ext cythoning len_extern.pyx to len_extern.c building 'len_extern' extension creating build creating build\temp.win-amd64-2.7 creating build\temp.win-amd64-2.7\Release C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Miniconda\include -IC:\Miniconda\PC -c len_extern.c -o build\temp.win-amd64-2.7\Release\len_extern.o writing build\temp.win-amd64-2.7\Release\len_extern.def C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -shared -s build\temp.win-amd64-2.7\Release\len_extern.o build\temp.win-amd64-2.7\Release\len_extern.def -LC:\Miniconda\libs -LC:\Miniconda\PCbuild\amd64 -lpython27 -lmsvcr90 -o "C:\Users\Jin\Documents\Git\python-tutorial\07\. interfacing with other languages\len_extern.pyd" ``` 从 `Python` 中调用: In [7]: ```py import len_extern ``` 调用这个模块后,并不能直接使用 `strlen` 函数,可以看到,这个模块中并没有 `strlen` 这个函数: In [8]: ```py dir(len_extern) ``` Out[8]: ```py ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__test__', 'get_len'] ``` 不过可以调用 `get_len` 函数: In [9]: ```py len_extern.get_len('hello') ``` Out[9]: ```py 5 ``` 因为调用的是 `C` 函数,所以函数的表现与 `C` 语言的用法一致,例如 `C` 语言以 `\0` 为字符串的结束符,所以会出现这样的情况: In [10]: ```py len_extern.get_len('hello\0world!') ``` Out[10]: ```py 5 ``` 除了对已有的 `C` 函数进行调用,还可以对已有的 `C` 结构体进行调用和修改: In [11]: ```py %%file time_extern.pyx cdef extern from "time.h": struct tm: int tm_mday int tm_mon int tm_year ctypedef long time_t tm* localtime(time_t *timer) time_t time(time_t *tloc) def get_date(): """Return a tuple with the current day, month and year.""" cdef time_t t cdef tm* ts t = time(NULL) ts = localtime(&t) return ts.tm_mday, ts.tm_mon + 1, ts.tm_year + 1900 ``` ```py Writing time_extern.pyx ``` 这里我们只使用 `tm` 结构体的年月日信息,所以只声明了要用了三个属性。 In [12]: ```py %%file setup_time_extern.py from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( ext_modules=[ Extension("time_extern", ["time_extern.pyx"]) ], cmdclass = {'build_ext': build_ext} ) ``` ```py Writing setup_time_extern.py ``` 编译: In [13]: ```py !python setup_time_extern.py build_ext --inplace ``` ```py running build_ext cythoning time_extern.pyx to time_extern.c building 'time_extern' extension C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Miniconda\include -IC:\Miniconda\PC -c time_extern.c -o build\temp.win-amd64-2.7\Release\time_extern.o writing build\temp.win-amd64-2.7\Release\time_extern.def C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -shared -s build\temp.win-amd64-2.7\Release\time_extern.o build\temp.win-amd64-2.7\Release\time_extern.def -LC:\Miniconda\libs -LC:\Miniconda\PCbuild\amd64 -lpython27 -lmsvcr90 -o "C:\Users\Jin\Documents\Git\python-tutorial\07\. interfacing with other languages\time_extern.pyd" ``` 测试: In [14]: ```py import time_extern time_extern.get_date() ``` Out[14]: ```py (19, 9, 2015) ``` 清理文件: In [15]: ```py import zipfile f = zipfile.ZipFile('07-04-extern.zip','w',zipfile.ZIP_DEFLATED) names = ['setup_len_extern.py', 'len_extern.pyx', 'setup_time_extern.py', 'time_extern.pyx'] for name in names: f.write(name) f.close() !rm -f setup*.* !rm -f len_extern.* !rm -f time_extern.* !rm -rf build ```