mirror of
https://github.com/apachecn/ailearning.git
synced 2026-02-10 13:55:05 +08:00
335 lines
5.9 KiB
Markdown
335 lines
5.9 KiB
Markdown
# 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
|
||
|
||
``` |