Cython:Cython 语法,调用其他C库
Cython 语法
cdef 关键词
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 的定义。
def, cdef, cpdef 函数
Cython
一共有三种定义方式,def, cdef, cpdef
三种:
def
- Python, Cython 都可以调用cdef
- 更快,只能 Cython 调用,可以使用指针cpdef
- Python, Cython 都可以调用,不能使用指针
cimport
In [1]:
- from math import sin as pysin
- from numpy import sin as npsin
In [2]:
- %load_ext Cython
从标准 C
语言库中调用模块,cimport
只能在 Cython
中使用:
In [3]:
- %%cython
- from libc.math cimport sin
- from libc.stdlib cimport malloc, free
cimport 和 pxd 文件
如果想在多个文件中复用 Cython
代码,可以定义一个 .pxd
文件(相当于头文件 .h
)定义方法,这个文件对应于一个 .pyx
文件(相当于源文件 .c
),然后在其他的文件中使用 cimport
导入:
fib.pxd, fib.pyx
文件存在,那么可以这样调用:
- from fib cimport fib
还可以调用 C++
标准库和 Numpy C Api
中的文件:
- from libcpp.vector cimport vector
- cimport numpy as cnp
调用其他C库
从标准库 string.h
中调用 strlen
:
In [4]:
- %%file len_extern.pyx
- cdef extern from "string.h":
- int strlen(char *c)
- def get_len(char *message):
- return strlen(message)
- Writing len_extern.pyx
不过 Cython
不会自动扫描导入的头文件,所以要使用的函数必须再声明一遍:
In [5]:
- %%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
编译:
In [6]:
- !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
中调用:
In [7]:
- import len_extern
调用这个模块后,并不能直接使用 strlen
函数,可以看到,这个模块中并没有 strlen
这个函数:
In [8]:
- dir(len_extern)
Out[8]:
- ['__builtins__',
- '__doc__',
- '__file__',
- '__name__',
- '__package__',
- '__test__',
- 'get_len']
不过可以调用 get_len
函数:
In [9]:
- len_extern.get_len('hello')
Out[9]:
- 5
因为调用的是 C
函数,所以函数的表现与 C
语言的用法一致,例如 C
语言以 \0
为字符串的结束符,所以会出现这样的情况:
In [10]:
- len_extern.get_len('hello\0world!')
Out[10]:
- 5
除了对已有的 C
函数进行调用,还可以对已有的 C
结构体进行调用和修改:
In [11]:
- %%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
- Writing time_extern.pyx
这里我们只使用 tm
结构体的年月日信息,所以只声明了要用了三个属性。
In [12]:
- %%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
编译:
In [13]:
- !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"
测试:
In [14]:
- import time_extern
- time_extern.get_date()
Out[14]:
- (19, 9, 2015)
清理文件:
In [15]:
- 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