Cython中的类型系统

由于最近的几个项目都有接触到Cython的使用,也积累了一些Cython的经验,于是决定把上次的介绍续上(蜜汁发现刚好过了一年。。),将Cython的一些用法记录下来。这一些文章可以作为一些学习的参考,不过kick-start的话还是去看看之前的博文以及官方的例子吧~

Cython对C/C++内置类型的支持

Cython在类型设置上和C/C++是十分相似的,不仅默认支持基本C类型intfloatunsigned long等等以及他们的指针类型,还支持C的structunionenum以及C++的cppclass(即C++中的类)。需要注意的是在Cython中,定义C/C++对象都需要使用cdef关键字,或者使用cpdef关键字定义一个Python封装过的对象。另外Cython还通过ctypedef支持C/C++形式的typedef。下面是一些定义变量的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cdef int i, j, k # i,j,k是C中的int类型
cdef float f, g[42], *h

cdef struct Grail: # C形式的结构体
int age
float volume
cdef Grail *gp # 结构体指针,注意不是cdef struct Grail

cdef enum CheeseState: # C++形式的枚举类
hard = 1
soft = 2
runny = 3

cdef enum: # C形式的匿名枚举
tons_of_spam = 3

  • size_tPy_ssize_t也是受Cython语言支持的类型。如果想使用uint32_t形式的类型,则需要添加from libc.stdint cimport uint32_t形式的语句。
  • 这些变量的作用域定义与Python是相同的,但是cdef定义的对象是不能import到Python代码中的,而cpdef定义的对象则可以。

Cython对Python内置类型的支持

尽管Cython兼容Python的语法,但是我们还可以通过cdef来使得Python对象具有静态类型,以提高运行效率。Cython在除了支持Python内置的listdicttuple外,还支持直接声明Python的基本类型,这需要通过from cpython cimport int形式的语句来实现。此外有一类特殊的类型是ctuple,能够直接定义tuple中元素的类型与数量,有些类似于C# 7中的语法,例子如下:

1
2
3
4
5
6
7
cdef dict sd # 直接定义内置类型,实际是PyDict对象

from cpython cimport int as pyint
cdef pyint big_a # PyInt对象

from libcpp cimport bool
cdef (int, unsigned long, bool) table # ctuple对象

Cython的类型映射

在将Python的基本类型对象与C/C++的基本类型对象进行相互赋值的过程中,Cython会进行自动的类型转换,可识别的转换规则有:

C typesFrom Python typesTo Python types
[unsigned] char, [unsigned] short, int, longint, longint
unsigned int, unsigned long, [unsigned] long longint, longlong
float, double, long doubleint, long, floatfloat
char*[1]str/bytesstr/bytes
C array[2]iterablelist
structuniondict

[1] Python2中转换成str,Python3中转换成bytes [2] char数组除外

如果自动类型转换不被支持、或者自动转换类型不是所需类型的话,还可以使用强制类型转换,语法是在变量前加<type-name>,例如

1
2
3
4
5
6
7
8
9
from cpython.ref cimport PyObject

cdef extern from *:
ctypedef Py_ssize_t Py_intptr_t

python_string = "foo"

cdef void* ptr = <void*>python_string
cdef Py_intptr_t adress_in_c = <Py_intptr_t>ptr

Cython中的模板

Cython还支持C++中的模板,语法是class_name[template_args],不过模板参数目前只支持类型参数,因此更像是C#中的泛型。对模板的支持不仅是可以声明模板类,还可以支持绑定已有C++的模板类,这也是Cython区别于Boost.Python、Pybind11等C++端绑定库的重要一点。由于后者在编译期无法了解Python代码的使用需求,因此只能在编译器展开模板(Instantiation),而Cython则可以通过.pxd头文件保留模板的格式,在引用该库需要编译时再展开。

模板的应用例子如下:

1
2
3
4
5
6
7
from libcpp.vector cimport vector

cdef vector[int] vect
cdef int i, x

for i in range(10):
vect.push_back(i)

参考内容:
Cython文档
Cython文档 - Language Basics

Shoot me some coffee money XD
0%