如何测试 Cython 属性 是否为生成器?

How to test if Cython property is generator?

在IPython中,我可以看到一个Cython class的属性是一个生成器,只需定义它然后调用:

%%cython
cdef class SomeCls:
    property x:
        def __get__(self):
            yield 1

调用看起来像

SomeCls().x
# prints <generator at 0x102f61ee8>

我无法测试 属性 是否是一个生成器:

import types
print(isinstance(SomeCls().x, types.GeneratorType))
# prints False

import inspect
print(inspect.isgeneratorfunction(SomeCls.x))
# prints False

如何确定 Cython class 的 属性 是否是生成器?

为什么通常的方法不起作用?

首先,您可能已经知道,inspect.isgeneratorfunction(...)isinstance(..., types.GeneratorType) 之间没有区别 - 检查模块只是 calls isinstance(..., types.GeneratorType).

另一方面,types.GeneratorTypedefined as

def _g():
    yield 1
GeneratorType = type(_g())

CPython 使用 PyGenObject (here code, here documentation) 作为生成器,没有像一些 ABC-类 那样花哨的比较逻辑,所以 isinstance 将归结为比较C 对象类型。

然而,Cython returns 一个 __pyx_CoroutineObject 用于生成器(只需检查 cythonized 代码即可查看)

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;

isinstance 而言,这与 PyGenObject 无关 - 它并不真正关心 generator 是否在类型名称中(但对我们而言人类这真的很令人费解,因为 type(obj) 说 "generator")。

因此您将不得不推出自己的 isgenerator 版本,这也考虑到了 Cython-"generators"。方法有很多种,比如

%%cython
def _f():
    yield 1
CyGeneratorType = type(_f())   
def iscygenerator(o):
    return isinstance(o, CyGeneratorType)

现在:

import inspect   
def isgenerator(o):
    return inspect.isgenerator(o)  or iscygenerator(o)

isgenerator(SomeCls().x)          #  True
iscygenerator(SomeCls().x)        #  True
inspect.isgenerator(SomeCls().x)  #  False