Python 的 for 和 while 循环是灵活并且高级的,语法自然、读起来像伪代码。而 Cython 也支持 for 和 while,无需修改。但由于循环通常占据程序运行时的大部分时间,因此我们可以通过一些优化,确保 Cython 能够将 Python 循环转换为高效的 C 循环。
n = 100 for i in range(n): ...
上面是一个标准的 Python for 循环,如果这个 i 和 n 是静态类型,那么 Cython 就能生成更快的 C 代码。
cdef Py_ssize_t i, n = 100 for i in range(n): ... # 这段代码和下面的C代码是等效的 """ for (i=0; i<n; ++i) { /* ... */ } """
所以当通过 range 进行循环时,我们应该将 range 里面的参数以及循环变量换成 C 的整型。如果不显式地进行静态声明的话,Cython 就会采用最保守的策略:
cdef Py_ssize_t n = 100 for i in range(n): print(i + 2 ** 100)
在循环的时候,如果我们使用了变量 i,那么在和一个数字相加的时候,由于 Cython 无法确定是否会发生溢出,因此会保守的选择 Python 的整型。
如果我们能保证表达式中一定不会发生溢出,那么可以显式地将 i 也声明为 C 的整数类型。
当然不光是整型,其它的 Python 类型也可以提前声明,举个例子:
cdef list lst = [ {"name": "satori", "age": 17}, {"name": "koishi", "age": 16}, {"name": "marisa", "age": 15}, ] # lst 里面都是字典,在遍历之前可以提前声明好 cdef dict item for item in lst: print(f"{item['name']}, {item['age']}") """ satori, 17 koishi, 16 marisa, 15 """ # 通过 cdef dict item 提前声明循环变量的类型 # 然后遍历以及操作的时候,速度会快很多 # 因为我们实现了基于类型的优化
以上是 for 循环,至于 while 循环也是同理,说白了还是规定好类型,实现基于类型的优化。
当然目前的优化还只是一部分,我们将在后续系列中了解优化循环体的更多信息,包括 Numpy 在 Cython 中的使用以及类型化内存视图。
循环的另一种方式
对于 Cython 而言,循环还有另一种方式,不过已经过时了,不建议使用,了解一下即可:
cdef int i # 等价于 for i in range(0, 5) # 不可以写成 for i from i >=0 and i < 5 for i from 0<= i < 5: print(i) """ 0 1 2 3 4 """ # for i in range(0, 5, 2) for i from 0 <= i < 5 by 2: print(i) """ 0 2 4 """
这种循环在语法上看起来很酷,但是已经过时了,因此直接使用 range 即可。