Как проверить, является ли объект объектом-генератором в python?

157

В Python, как проверить, является ли объект объектом-генератором?

Пробую это -

>>> type(myobject, generator)

выдает ошибку -

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'generator' is not defined

(Я знаю, что могу проверить, есть ли у объекта nextметод для того, чтобы он был генератором, но мне нужен какой-то способ, с помощью которого я могу определить тип любого объекта, а не только генераторов.)

Пушпак Дагаде
источник
4
Какую актуальную проблему вы пытаетесь решить? Опубликовать больше контекста, может быть, умнее. Зачем вам нужно знать, если это генератор?
Дейнит
7
from types import GeneratorType;type(myobject, GeneratorType)даст вам правильный результат для объектов класса «генератор». Но, как предполагает Дейнит, это не обязательно правильный путь.
JAB
7
Если вы проверяете __next__, вы фактически принимаете любой итератор, а не только генераторы - что весьма вероятно, что вы хотите.
2
Как часто, так и нет, реальная цель узнать, является ли что-то генератором, состоит в том, чтобы избежать их, из-за желания многократно повторять одну и ту же коллекцию.
Ян
2
Для людей, интересующихся вариантом использования, это может быть полезно, когда вам нужно знать, будет ли использован итератор (например, если ваша функция принимает какой-либо итератор, но нуждается в повторении более одного раза, вы захотите материализовать его перед выполнением итерации)
wbadart

Ответы:

227

Вы можете использовать GeneratorType из типов:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True
utdemir
источник
5
К сожалению, это не работает для классов-генераторов (например, объектов карты или фильтра).
Рикардо Крус
Возможно isinstance(gen, (types.GeneratorType, map, filter)), полезно также обнаружить mapи filter. Это все еще не будет включать в себя другие итерации и итераторы, хотя.
JLH
38

Вы имеете в виду функции генератора? использовать inspect.isgeneratorfunction.

РЕДАКТИРОВАТЬ :

если вам нужен объект генератора, вы можете использовать inspect.isgenerator, как указал JAB в своем комментарии.

mouad
источник
1
функция генератора не является объектом генератора; см. ответ @ utdemir
Петр Финдейзен
5
@Piotr: в каком случае вы используете inspect.isgenerator.
JAB
@JAB, @Piotr: решил рассмотреть все возможности того, что может означать ОП, спасибо JAB :)
mouad
1
Примечание: если вам нужен только этот тест, вы можете избежать небольшие накладные расходы при использовании @utdemir решение , потому что inspect.isgeneratorэто только сокращенная на: isinstance(object, types.GeneratorType).
bufh
См. Ответ @RobertLujo для различия между объектом-генератором и функцией-генератором. stackoverflow.com/a/32380774/3595112
industryworker3595112
24

Я думаю, что важно проводить различие между функциями генератора и генераторами (результат функции генератора):

>>> def generator_function():
...     yield 1
...     yield 2
...
>>> import inspect
>>> inspect.isgeneratorfunction(generator_function)
True

Вызов generator_function не даст нормального результата, он даже не выполнит никакого кода в самой функции, результатом будет специальный объект, называемый generator :

>>> generator = generator_function()
>>> generator
<generator object generator_function at 0x10b3f2b90>

так что это не функция генератора, а генератор:

>>> inspect.isgeneratorfunction(generator)
False

>>> import types
>>> isinstance(generator, types.GeneratorType)
True

и функция генератора не является генератором:

>>> isinstance(generator_function, types.GeneratorType)
False

просто для справки, фактический вызов тела функции будет происходить при использовании генератора, например:

>>> list(generator)
[1, 2]

Смотрите также. В python есть ли способ проверить, является ли функция «генератором», прежде чем вызывать ее?

Роберт Лужо
источник
11

inspect.isgeneratorФункция хорошо , если вы хотите , чтобы проверить наличие чистых генераторов (т.е. объекты из «генератора» класса). Однако он вернется, Falseесли вы проверите, например, izipитерацию. Альтернативный способ проверки обобщенного генератора - использовать эту функцию:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')
Лука Сбарделла
источник
1
Хм. Это возвращает истину для x=iter([1,2]). Мне кажется, это действительно проверка, является ли объект итератором , а не генератором. Но, возможно, «итератор» - это именно то, что вы подразумеваете под «обобщенным генератором».
Джош О'Брайен,
3

Вы можете использовать Итератор или, более конкретно, Генератор из модуля ввода .

from typing import Generator, Iterator
g = (i for i in range(1_000_000))
print(type(g))
print(isinstance(g, Generator))
print(isinstance(g, Iterator))

результат:

<class 'generator'>
True
True
user9074332
источник
1
+1 за рабочий раствор. При этом документы для typing.TypeVarкласса, по-видимому, не поощряют использование isinstanceв сочетании с typingмодулем: «Во время выполнения isinstance(x, T)будет повышаться TypeError. В общем, isinstance()и issubclass()не должно использоваться с типами».
Яша
2
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True
Кори Голдберг
источник
Это работает, только если это функция. Если 'foo' является объектом генератора, он показывает 'Ложь'. Смотрите мой вопрос, я хочу сделать проверки для объектов генератора.
Пушпак Дагаде
2

Я знаю, что могу проверить, есть ли у объекта следующий метод, чтобы он был генератором, но мне нужен какой-то способ, с помощью которого я могу определить тип любого объекта, а не только генератора.

Не делай этого. Это просто очень, очень плохая идея.

Вместо этого сделайте это:

try:
    # Attempt to see if you have an iterable object.
    for i in some_thing_which_may_be_a_generator:
        # The real work on `i`
except TypeError:
     # some_thing_which_may_be_a_generator isn't actually a generator
     # do something else

В маловероятном случае, когда в теле цикла for также есть TypeErrors, есть несколько вариантов: (1) определить функцию для ограничения объема ошибок или (2) использовать вложенный блок try .

Или (3) что-то вроде этого, чтобы различать все эти, TypeErrorкоторые плавают вокруг.

try:
    # Attempt to see if you have an iterable object.
    # In the case of a generator or iterator iter simply 
    # returns the value it was passed.
    iterator = iter(some_thing_which_may_be_a_generator)
except TypeError:
     # some_thing_which_may_be_a_generator isn't actually a generator
     # do something else
else:
    for i in iterator:
         # the real work on `i`

Или (4) исправьте другие части вашего приложения, чтобы обеспечить генераторы соответствующим образом. Это часто проще, чем все это.

С. Лотт
источник
1
Ваше решение будет перехватывать TypeErrors, выдаваемые телом цикла for. Я предложил редактирование, которое предотвратит это нежелательное поведение.
Дюны
Это более Pythonic способ сделать это, если я не ошибаюсь.
JAB
Хотя, если вы перебираете список элементов, и большинство из них не являются итераторами, чем итераторы, то это может занять больше времени?
Якоб Бойер
1
@Jakob Bowyer: Исключения быстрее ifзаявлений. И. Такая микрооптимизация - пустая трата времени. Исправьте алгоритм, который создает смешанный пакет итераторов и не итераторов, чтобы производить только итераторы и избавить себя от всей этой боли.
S.Lott
10
Это было бы ошибочно принимать любой итеративный как генератор.
Балки
1

Если вы используете веб-сервер Tornado или аналогичный, вы можете обнаружить, что методы сервера на самом деле являются генераторами, а не методами. Это затрудняет вызов других методов, так как yield не работает внутри метода, и поэтому вам нужно начать управлять пулами связанных объектов-генераторов. Простой способ управления пулами связанных генераторов состоит в создании вспомогательной функции, такой как

def chainPool(*arg):
    for f in arg:
      if(hasattr(f,"__iter__")):
          for e in f:
             yield e
      else:
         yield f

Теперь пишу цепочечные генераторы, такие как

[x for x in chainPool(chainPool(1,2),3,4,chainPool(5,chainPool(6)))]

Производит вывод

[1, 2, 3, 4, 5, 6]

Это, вероятно, то, что вы хотите, если вы хотите использовать генераторы в качестве альтернативы потока или подобного.

user6830669
источник
1

(Я знаю, что это старый пост.) Нет необходимости импортировать модуль, вы можете объявить объект для сравнения в начале программы:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
Kantal
источник