本文最后更新于 428 天前,其中的信息可能已经过时,如有错误请发送邮件到 wuxianglongblog@163.com
cdef
定义 C
类型变量。
可以定义局部变量:
| def fib(int n): |
| cdef int a,b,i |
| ... |
定义函数返回值:
| 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 |
定义函数:
| cdef class Particle(object): |
| cdef float psn[3], vel[3] |
| cdef int id |
注意函数的参数不需要使用 cdef 的定义。
Cython
一共有三种定义方式,def, cdef, cpdef
三种:
def
- Python, Cython 都可以调用
cdef
- 更快,只能 Cython 调用,可以使用指针
cpdef
- Python, Cython 都可以调用,不能使用指针
| from math import sin as pysin |
| from numpy import sin as npsin |
从标准 C
语言库中调用模块,cimport
只能在 Cython
中使用:
| %%cython |
| from libc.math cimport sin |
| from libc.stdlib cimport malloc, free |
如果想在多个文件中复用 Cython
代码,可以定义一个 .pxd
文件(相当于头文件 .h
)定义方法,这个文件对应于一个 .pyx
文件(相当于源文件 .c
),然后在其他的文件中使用 cimport
导入:
fib.pxd, fib.pyx
文件存在,那么可以这样调用:
还可以调用 C++
标准库和 Numpy C Api
中的文件:
| from libcpp.vector cimport vector |
| cimport numpy as cnp |
从标准库 string.h
中调用 strlen
:
| %%file len_extern.pyx |
| cdef extern from "string.h": |
| int strlen(char *c) |
| |
| def get_len(char *message): |
| return strlen(message) |
不过 Cython
不会自动扫描导入的头文件,所以要使用的函数必须再声明一遍:
| %%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} |
| ) |
| Writing setup_len_extern.py |
编译:
| !python setup_len_extern.py build_ext --inplace |
| 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
中调用:
调用这个模块后,并不能直接使用 strlen
函数,可以看到,这个模块中并没有 strlen
这个函数:
| ['__builtins__', |
| '__doc__', |
| '__file__', |
| '__name__', |
| '__package__', |
| '__test__', |
| 'get_len'] |
不过可以调用 get_len
函数:
| len_extern.get_len('hello') |
因为调用的是 C
函数,所以函数的表现与 C
语言的用法一致,例如 C
语言以 \0
为字符串的结束符,所以会出现这样的情况:
| len_extern.get_len('hello\0world!') |
除了对已有的 C
函数进行调用,还可以对已有的 C
结构体进行调用和修改:
| %%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 |
这里我们只使用 tm
结构体的年月日信息,所以只声明了要用了三个属性。
| %%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} |
| ) |
| Writing setup_time_extern.py |
编译:
| !python setup_time_extern.py build_ext --inplace |
| 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" |
测试:
| import time_extern |
| |
| time_extern.get_date() |
清理文件:
| 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 |