Files
ailearning/docs/da/095.md
2020-10-19 21:08:55 +08:00

335 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CythonCython 语法调用其他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
```