Почему не работает обычный способ?
Во-первых, как вы, наверное, уже знаете, нет разницы между inspect.isgeneratorfunction(...)
и isinstance(..., types.GeneratorType)
- модуль проверки просто вызывает isinstance(..., types.GeneratorType)
.
Вкл.с другой стороны, types.GeneratorType
- это , определенный как
def _g():
yield 1
GeneratorType = type(_g())
CPython использует PyGenObject ( здесь код , здесь документация ) для генераторов,для сравнения нет причудливой логики, как для некоторых ABC-классов, поэтому isinstance
будет сводиться к сравнению типов объектов C.
Однако Cython возвращает __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;
, который не имеет ничего общего с PyGenObject
в отношении isinstance
- на самом деле все равно, является ли generator
во имя типа (но для нас, людей, это может быть действительно загадочно, потому что type(obj)
говорит "генератор").
Так что вам придется развернуть свою собственную версию isgenerator
, которая требуеттакже Cython - «генераторы» во внимание.Есть много способов, например
%%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