1. Cython纯净模式
所谓的纯净模式就是cython直接编译.py文件的模式.
在某些情况下,希望加速Python代码,而不会失去与Python解释器一起运行的能力.可以使用Cython编译纯Python脚本,但通常只能以20%-50%的速度增长.
为了超越此范围,Cython提供了语言结构,为Python模块添加静态类型和cythonic功能,使其在编译时运行得更快,同时仍然允许它被解释.有两种方案可以实现这一需求:
- 通过导入cython模块后使用其中的特殊功能和装饰器完成
- 通过扩展的.pxd文件
尽管通常不建议在.pyx文件中直接编写Cython代码,但更容易的测试,可以方便的与纯Python开发人员的协作等特点也为其提供了合理性.
在纯净python源文件下,你或多或少受限于在Python中的语法表达,如果希望跳过python语法,只能用具有扩展语言语法的.pyx文件完成,因为它依赖于Cython编译器的特性.
cython可以使用cython.compiled来反射是否运行的是被编译的版本
.pxd文件中可以申明使用C/C++扩展,因此可以在.py文件中根据需要使用c/c++的编写的函数.
1.1. 通过cython模块中的特殊功能和装饰器扩展
这种方式相比前面的使用pxd申明扩展的方式更加方便,所有这些特殊功能和装饰器都是非侵入式的,加上去掉都不会影响原本.py源代码在python解释器中的工作.
1.1.1. 可用于申明的C类型
cython中常用的C语言类型有:
| cython中类型 | 类型说明 | 
|---|---|
| cython.int | 整形 | 
| cython.long | 长整形 | 
| cython.double | 双精度浮点型 | 
| cython.char | 字符型 | 
其他的包括无符号整形之类的C语言中的基础类型也都有,这边不再复述.
另外一个特殊的类型就是指针,cython中指针类型就是基础类型前面加上p_,如整形数指针就是cython.p_int
1.1.2. 常用的声明函数和装饰器有
不同于.pyx文件,.py文件需要符合python语法,因此很多申明和关键字需要使用函数或者类等对象来代替,纯净模式更多的是静态化参数以此来提高效率.下面是用于申明的函数和装饰器
- cython.declare(**kws)- 用于申明变量 
- cython.struct(**kws)- 申明一个结构体 
- cython.union(**kws)- 申明一个联合体 
- @cython.locals(**kws)- 用于声明函数或者方法的参数和内部变量,即便是python方法也可以声明变量类型,这样静态化也可以获得提速 
- @cython.returns([cython_type])- 用于申明函数或者方法的参数和内部变量 
- @cython.ccall- 申明可被python解释器调用的cython函数.相当于 - .pyx中的- cpdef定义的函数或方法
- @cython.cfunc- 申明c/c++语言函数,这种函数会跳过运行时直接执行,而且隐藏在python解释器之下,只有模块中才可以调用 
- @cython.inline- 申明函数为 - inline函数
1.1.3. 常用的特殊函数
处理C语言函数时,我们很有可能需要使用一些关于内存指针的操作,这些操作python自己是没有的,因此Cython也提供一些这种特殊的函数
- cython.address(x)- 获取变量的指针地址 
- cython.sizeof(x)- 获取变量的地址空间大小 
- cython.typedef(x)- 用于获取一个给定指针名称下的变量类型的类型 
- cython.cast(T,x,typecheck=True)- 用于将变量指定为某一类型,类似C/C++语言中的 - <T>t.注意这个函数是不安全的,容易内存泄漏.可选参数- typecheck=True相当于- <T?>t
1.1.4. 常用编译指示装饰器
- @cython.boundscheck(bool)- 用于设定是否进行边界检查,默认值为True。 
- @cython.wraparound(bool)- 用于设定是否进行数组负索引检查,默认值为True。 
- @cython.initializedcheck(bool)- 用于设定访问或分配内存视图时是否检查它是否被初始化.默认值为True。 
- @cython.overflowcheck- 如果设置为True,当溢出的C整数算术运算上引发了异常时,会执行适度的运行时惩罚,但即便如此还是比python的int运算快很多,默认为False 
- @cython.overflowcheck.fold- 如果设置为True,并且overflowcheck为True,则检查嵌套的溢出位,和有副作用的算术表达式,而不是每个步骤都检查。 依赖于不同的编译器,体系结构和优化设置,这项选项可能有助于提高性能也可能损害性能。 默认值为True。 
- @cython.nonecheck- 如果设置为False,Cython可以自由地假定 对变量类型的本地字段访问为扩展类型,或者 当变量被设为None时,对缓冲区变量的缓冲区访问永远不会发生。否则插入一个检查并引发适当的异常。 
这些装饰器可以放在函数上标记好
1.1.5. 常用的编译器指示注释
编译器可以识别在源文件开始部分的注释为全局的编译器指示,常见的有
- #cython: language_level=3标记编译目标为python3
- #cython: boundscheck = False设置全局不进行边界检查
编译器设置的参数可以在前面的Cython基本流程部分看到
%%writefile B.py #cython: language_level=3 import cython  if cython.compiled:     print("Yep, I'm compiled.") else:     print("Just a lowly interpreted script.")  @cython.boundscheck(False) @cython.ccall @cython.returns(cython.int) @cython.locals(x=cython.int, y=cython.int,a = cython.int) def myfunction(x, y=2):     a = x-y     return a + x * y  @cython.cfunc @cython.returns(cython.double) @cython.locals(a = cython.double) def _helper(a):     return a + 1  @cython.cclass class A:     cython.declare(a=cython.int, b=cython.int)     def __init__(self, b=0):         self.a = 3         self.b = b     @cython.ccall     @cython.locals(x=cython.double)     def foo(self, x):         print(x + _helper(1.0)) Overwriting B.py import B Just a lowly interpreted script. B.myfunction(10) 28 a = B.A() a.foo(2.7) 4.7 %%writefile setup.py  from distutils.core import setup from Cython.Build import cythonize from Cython.Distutils import build_ext setup(     cmdclass = {'build_ext': build_ext},     ext_modules = cythonize("B.py",language="c++") ) Overwriting setup.py Overwriting B.py 0Overwriting B.py 1import B Overwriting B.py 3B.myfunction(10) 28 a = B.A() a.foo(2.7) 4.7 Overwriting B.py 81.2. cython纯净模式福利--类型自动转换
| C 类型 | 从pyhton中获得 | 返回到python中 | 
|---|---|---|
| [unsigned] char, [unsigned] short, int, long | int | int | 
| unsigned int, unsigned long, [unsigned] long long | int | int | 
| float, double, long double | int, float | float | 
| char* | bytes | bytes | 
| struct, union | --- | dict | 
除此之外同构定长列表/元组可以与c中数组自动转化
Overwriting B.py 91.2.1. 纯python文件扩展的局限性
这种方式的缺点在于:
- 语法不优雅,堆叠3个装饰器来装饰一个函数看起来很不美观
- 默认无法使用type hint,测试结果看cython解释器无法支持type hint,这个特性会在后续的版本中改善
1.3. 使用.pxd申明要用的C/C++函数
使用上面这种方式有个很大的缺陷就是无法使用C/C++写好的函数,要让这个可行需要有一个.pxd文件用于声明用到的函数,并将其包装为cpdef的形式
import B 0import B 1import B 2import B 3import B 4Just a lowly interpreted script. import B 6import B 7import B 8Overwriting setup.py Overwriting B.py 0Just a lowly interpreted script. 1import B 4Overwriting B.py 3import B 6Just a lowly interpreted script. 51.3.1. 将所有申明迁移至.pxd文件
像上面这种写法已经引入了一个新的文件,既然如此为什么不把申明用的这些个装饰器都移到.pxd文件中增加可读性呢
下面是一个纯python源文件
Just a lowly interpreted script. 6Just a lowly interpreted script. 7Just a lowly interpreted script. 8Just a lowly interpreted script. B.myfunction(10) 028 B.myfunction(10) 2B.myfunction(10) 3使用.pxd为其申明
B.myfunction(10) 4B.myfunction(10) 5那么Cython将会将A.py编译成如下:
B.myfunction(10) 6注意:
- 使用*通配符可以将Python的参数默认值包装给.pxd中的定义,即可以从Python访问
cpdef int myfunction(int x, int y=*)
- 内部函数的C函数签名可以声明为cdef
cdef double _helper(double a)
- cdef class用于申明扩展类型 
- 如果属性有读取/写入Python访问权限,则cdef类属性必须声明为cdef public,cdef readonly为只读Python访问,或者是纯Cdef为内部C级属性 
- cdef class 中方法必须声明为 - cpdef Python可见方法
- cdef用于内部C方法
 
B.myfunction(10) 7Overwriting setup.py Overwriting B.py 028 0Just a lowly interpreted script. 8Overwriting B.py 328 328 4B.myfunction(10) 228 61.3.2. 使用.pxd申明扩展纯python的局限性
使用.pxd扩展.py源文件的方式最大的缺点在于可维护性,一旦有改动那么无论.py和.pxd文件都得改动.而且因为使用cython语法定义.pxd文件,所以对于不会cython的用户很不友好.
另一局限性在于使用c/c++函数或者标准库时只能在.pxd文件下申明,因此必须在.py文件中判断是否被编译,而且必须实现一个同名的python对象来为纯python环境提供支持.
1.4. 纯净模式的局限性
无论是否要使用.pxd申明文件,纯净模式都无法在实现上使用C++中STL容器和算法,也有很多cython语言特性无法实现.
1.5. 使用typehits申明静态类型
在python3.5发布后,cython社区也提出了使用type hint来申明cython纯净模式的提议-- Python Typing Proposal.在0.27版本以后,cython已经完全支持这种静态类型申明的方式了.纯净模式代码可以比之前的优雅不少
28 728 828 9Just a lowly interpreted script. a = B.A() a.foo(2.7) 128 a = B.A() a.foo(2.7) 34.7 a = B.A() a.foo(2.7) 5a = B.A() a.foo(2.7) 628 9Overwriting B.py 3a = B.A() a.foo(2.7) 128 a = B.A() a.foo(2.7) 34.7 2


 
		 
		 
		

还没有评论,来说两句吧...