Python 如何在Cython中键入生成器函数?

Python 如何在Cython中键入生成器函数?,python,cython,Python,Cython,如果我有Python中的生成器函数,请说: def gen(x): for i in range(x): yield(i ** 2) 如何在Cython中声明输出数据类型为int?值得花点时间吗 谢谢 编辑:我读到变更日志中提到的(异步)生成器: 但是,没有关于如何使用它们的文档。这是因为它们受支持,但在Cython中使用它们没有特别的优势,或者不可能进行优化?不,在Cython中没有这样做的方法 当您查看Cython生成的代码时,您将看到gen(和其他生成器函数)返回

如果我有Python中的生成器函数,请说:

def gen(x):
    for i in range(x):
        yield(i ** 2)
如何在Cython中声明输出数据类型为
int
?值得花点时间吗

谢谢

编辑:我读到变更日志中提到的(异步)生成器:


但是,没有关于如何使用它们的文档。这是因为它们受支持,但在Cython中使用它们没有特别的优势,或者不可能进行优化?

不,在Cython中没有这样做的方法

当您查看Cython生成的代码时,您将看到
gen
(和其他生成器函数)返回一个生成器,它基本上是一个
\uuuupyx\uCoroutineObject
对象,它:

最重要的部分是
主体
-成员:这是执行实际计算的函数。正如我们所看到的,它返回一个
PyObject
,并且没有办法(还?)将其调整为
int
double
或类似的格式

至于没有这样做的原因,我只能猜测——但可能不止一个原因

如果您真的关心性能,那么生成器无论如何都会引入太多的开销(例如,
yield
cdef
-函数中是不可能的),应该重构为更简单的函数


详细说明可能的重构。作为基线,我们假设我们要总结所有创建的值:

%%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类),这将提供额外的快速/高效的可能性来获取
int
-值,而不是
PyObjects
,但这一切都取决于您手头的场景。我只是想表明,通过丢弃发电机,有可能提高性能。

谢谢你的回答。我的印象是,发电机通常效率更高,至少在内存方面。所以,如果我要重构我的函数以返回,比如说一个集合或一个列表,我应该声明哪种返回类型?@user3758232我详细阐述了我所说的“重构”的含义。如果返回整个数据,我会选择
array.array
或numpy数组,因为它们存储的不是Python对象,而是原始整数/双精度数等等-所需内存更少,速度更快。非常非常有用。谢谢实际上,我已经有了一个内部函数,我可以按照你建议的方式优化它,还有一个外部循环,它也可以被多次调用。为此,我可以查看数组。
>>> %timeit sum_it_fast(1000)
661 ns ± 20.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)