目录
这次来介绍一下Cython中的特殊函数定义,Cython相比Python本身的特殊函数之外还增加了一些新的函数,用来满足对C特性的支持,其中有些内容还经常令人混淆。关于Python中特殊变量和特殊函数名的内容,请参考Python官方文档。
def
, cdef
和cpdef
首先最开始需要分清的便是Cython中的三种函数类型。def
定义的对象(包括变量、函数、类型)都是普通的Python对象,是Python可以直接调用的,因此其参数都只能是Python类型或对象;cdef
定义的对象则是C/C++层面的,可以直接用C/C++对象作为参数,因此不能被普通Python代码调用,这样减少了很多overhead因此可以提高运行效率。另外尽管cdef
的函数不是Python对象,无法当作变量使用,但还是可以获取函数指针的。而cpdef
则是同时兼具两方面特性,其本质是用cdef
定义函数后再用def
定义一个函数封装,使得在Cython中调用时可以调用高效的cdef
版本,而在Python中调用的是与Python兼容的def
版本。
__init__
和__cinit__
在理清了上面几个关键字后另一个经常令人疑惑的点便是__init__
和__cinit__
的区别。__cinit__
和__dealloc__
都是Cython特有的特殊函数。官方文档在其用法上解释的并不清楚,只是说__cinit__
可以用来进行C/C++级别的初始化。实际上,使用__cinit__
的重要原因是源于其特性:__cinit__
会像C++一样自动执行基类的__cinit__
,因此它保证会在构造时被执行一次(且只被执行一次)。由于Python中的__init__
函数默认不会调用基类的__init__
,因此如果想保证类型中的cdef
成员被初始化,避免可能的堆栈问题(如指针没有初始化),那么就可以选择使用__cinit__
。如果理解了这一点就可以知道,什么时候需要使用__cinit__
了。
但是使用__cinit__
的时候有很多限制需要了解:
__cinit__
有时会带来额外的开销,这篇博客中有一些分析。__cinit__
的参数声明和__init__
必须一致,因为会同时被调用。因此通常__cinit__
的参数中会留下*kargs
和**kvargs
。Stackoverflow上也有人问过这个情况。__cinit__
中如果要用malloc
分配内存,记得在__dealloc__
中销毁。__dealloc__
相当于C++版本的__del__
__cinit__
和__init__
一样也只能使用def
声明,不能用__cdef__
和__cpdef__
。具体原因我并不清楚。
运算符重载
其他大多数的特殊函数定义和用法几乎和Python相同,但是需要特别指出的是运算符重载的部分。以加法为例,在Python中加法a + b
的实现方式是:
- 如果
a
中定义了__add__
,那么调用a.__add__(b)
- 如果
a
中没有定义,而b
中定义了__radd__
,那么调用b.__radd(a)
而在Python的C扩展类里(包含Cython和pybind11的实现),其实现方式是寻找接受a
和b
类型的__add__
重载,也就是说本质上在C扩展类中定义的__add__
都是__add__
的重载,这也是与C++的operator
重载理念一致,只不过这个__add__
仍然需要定义在类里。在Cython文档中给出的运算符列表里,参数里带self
的函数都是按照Python中的方法实现的,self
不能指定类型;而以x, y
这种形式为参数的则是按照C扩展类执行方式的函数,x
和y
都可以指定类型。
另外Cython还定义了一个特殊的运算符函数__richcmp__
,这个是Python中没有的,不过其功能只是把比较符号(>,<,=)的实现合并了,与Python的__eq__
、__lt__
等函数没有本质区别。这在官方文档中也有说明
__getbuffer__
Cython中有两个版本的Buffer协议,一种是提案PEP-3118定义的,另一种是Python官方定义之前Cython自己的定义方式。其中前者在之前介绍Cython封装的文章中已有介绍,就不多赘述。其相关的特殊函数是__getbuffer__
和__releasebuffer__
,这两个函数也都是Cython特有的。而后者比较难用,已经被标记为depricated废弃了,也不介绍了。
属性(property)
Cython中还提供了一套非常方便的属性定义方法。原本在Python中定义属性非常但疼,例如下面的代码定义了名为length
的属性,使得你可以通过square.length
的方法访问它
|
|
而在Cython中定义属性就更简单了,它除了支持上面的方法外还有另一种更加直观的定义方式(虽然这个方式也已经被标记为depricated了):
|
|
Cython的类型还有各种其他的奇奇怪怪的小特性,在Cython的这两篇文档里有详细介绍:Extension Types, Special Methods of Extension Types,仅供参考~