Как проверить, является ли свойство Cython генератором? - PullRequest
0 голосов
/ 21 ноября 2018

В IPython я вижу, что свойство класса Cython является генератором, просто определив его и затем вызвав:

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

Вызов выглядит как

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

IУ меня проблемы с проверкой, является ли это свойство генератором:

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

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

Как определить, является ли свойство класса Cython генератором?

1 Ответ

0 голосов
/ 22 ноября 2018

Почему не работает обычный способ?

Во-первых, как вы, наверное, уже знаете, нет разницы между 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
...