不,Cython中没有这样做的方法。
当您查看由Cython生成的代码时,您会发现gen
(以及其他生成器函数)返回一个生成器,它基本上是一个__pyx_CoroutineObject
对象,这个对象的结构可以在此处看到:
typedef PyObject *(*__pyx_coroutine_body_t)(PyObject *, PyThreadState *, PyObject *);
typedef struct {
PyObject_HEAD
__pyx_coroutine_body_t body;
PyObject *closure;
...
int resume_label;
char is_running;
} __pyx_CoroutineObject;
最重要的部分是 body
成员:这个函数执行实际的计算。正如我们所看到的,它返回一个 PyObject
,目前没有办法将其适配为 int
、double
或类似类型。
至于为什么不这样做,我只能猜测——但可能有不止一个原因。
如果你真的关心性能,那么生成器无论如何都会引入太多的开销(例如,在 cdef
函数中不可能使用 yield
),应该将其重构为更简单的形式。
更详细地阐述可能的重构方案。让我们假设我们想要对所有创建的值进行求和:
%%cython
def gen(int x):
cdef int i
for i in range(x):
yield(i ** 2)
def sum_it(int n):
cdef int i
cdef int res=0
for i in gen(n):
res+=i
return res
计时会导致:
>>> %timeit sum_it(1000)
28.9 µs ± 1.06 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
好消息是,它比纯Python版本快大约10倍,但如果我们真的追求速度:
%%cython
cdef int gen_fast(int i):
return i ** 2
def sum_it_fast(int n):
cdef int i
cdef int res=0
for i in range(n):
res+=gen_fast(i)
return res
它是:
>>> %timeit sum_it_fast(1000)
661 ns ± 20.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
大约快了50倍。
我明白,这是一个相当大的变化,可能很难做到 - 我只会在真正成为程序瓶颈时才这样做 - 但是加速50倍确实是一个真正的动力。
显然还有很多其他方法:使用numpy数组或array.array
代替生成器,或者编写自定义生成器(cdef-class),它将提供另一种获取int
-值而不是PyObjects
的快速/高效方式 - 但所有这些都取决于你手头的情况。我只想说明可以通过放弃生成器来提高性能。
array.array
或numpy数组,因为它们存储的不是Python对象,而是原始的int/double等数据类型 - 需要更少的内存并且速度更快。 - ead