Cython 中的类型系统
由于最近的几个项目都有接触到 Cython 的使用,也积累了一些 Cython 的经验,于是决定把上次的介绍续上(蜜汁发现刚好过了一年。。),将 Cython 的一些用法记录下来。这一些文章可以作为一些学习的参考,不过 kick-start 的话还是去看看之前的博文以及官方的例子吧~
Cython 对 C/C++ 内置类型的支持 ❮
Cython 在类型设置上和 C/C++ 是十分相似的,不仅默认支持基本 C 类型 int
、float
、unsigned long
等等以及他们的指针类型,还支持 C 的 struct
、union
、enum
以及 C++ 的 cppclass
(即 C++ 中的类)。需要注意的是在 Cython 中,定义 C/C++ 对象都需要使用 cdef 关键字,或者使用 cpdef 关键字定义一个 Python 封装过的对象。另外 Cython 还通过 ctypedef
支持 C/C++ 形式的 typedef。下面是一些定义变量的例子:
1 | cdef int i, j, k # i,j,k是C中的int类型 |
size_t
和Py_ssize_t
也是受 Cython 语言支持的类型。如果想使用uint32_t
形式的类型,则需要添加from libc.stdint cimport uint32_t
形式的语句。- 这些变量的作用域定义与 Python 是相同的,但是 cdef 定义的对象是不能 import 到 Python 代码中的,而 cpdef 定义的对象则可以。
Cython 对 Python 内置类型的支持 ❮
尽管 Cython 兼容 Python 的语法,但是我们还可以通过 cdef 来使得 Python 对象具有静态类型,以提高运行效率。Cython 在除了支持 Python 内置的 list
、dict
、tuple
外,还支持直接声明 Python 的基本类型,这需要通过 from cpython cimport int
形式的语句来实现。此外有一类特殊的类型是 ctuple
,能够直接定义 tuple 中元素的类型与数量,有些类似于 C# 7 中的语法,例子如下:
1 | cdef dict sd # 直接定义内置类型,实际是PyDict对象 |
Cython 的类型映射 ❮
在将 Python 的基本类型对象与 C/C++ 的基本类型对象进行相互赋值的过程中,Cython 会进行自动的类型转换,可识别的转换规则有:
C types | From Python types | To Python types |
---|---|---|
[unsigned] char, [unsigned] short, int, long | int, long | int |
unsigned int, unsigned long, [unsigned] long long | int, long | long |
float, double, long double | int, long, float | float |
char*[1] | str/bytes | str/bytes |
C array[2] | iterable | list |
struct | union | dict |
[1] Python2 中转换成 str,Python3 中转换成 bytes [2] char 数组除外
如果自动类型转换不被支持、或者自动转换类型不是所需类型的话,还可以使用强制类型转换,语法是在变量前加 <type-name>
,例如
1 | from cpython.ref cimport PyObject |
如果使用
<type-name?>
形式则会在转换时执行运行时检查
Cython 中的模板 ❮
Cython 还支持 C++ 中的模板,语法是 class_name[template_args]
,不过模板参数目前只支持类型参数,因此更像是 C# 中的泛型。对模板的支持不仅是可以声明模板类,还可以支持绑定已有 C++ 的模板类,这也是 Cython 区别于 Boost.Python、Pybind11 等 C++ 端绑定库的重要一点。由于后者在编译期无法了解 Python 代码的使用需求,因此只能在编译器展开模板(Instantiation),而 Cython 则可以通过.pxd
头文件保留模板的格式,在引用该库需要编译时再展开。
模板的应用例子如下:
1 | from libcpp.vector cimport vector |