Як перевірити, чи об’єкт є об’єктом генератора в 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
Яку актуальну проблему ви намагаєтеся вирішити? Опублікуйте більше контексту, може бути розумніший спосіб. Чому потрібно знати, чи це генератор?
Daenyth

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

5
Це, на жаль, не працює для генераторних класів (наприклад, об’єкти відображення чи фільтрування).
Рікардо Крус

Можливо isinstance(gen, (types.GeneratorType, map, filter)), корисно також виявити mapі filter. Це все ще не буде включати інші ітерабелі та ітератори.
jlh

38

Ви маєте на увазі функції генератора? використання inspect.isgeneratorfunction.

Редагувати:

якщо ви хочете об'єкт генератора, ви можете використовувати inspect.isgenerator, як вказав JAB у своєму коментарі.


1
функція генератора не є об'єктом генератора; див. відповідь @ utdemir
Piotr Findeisen

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_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]). Мені здається, це справді тестування того, чи є об’єкт ітератором , а не генератором. Але, можливо, "ітератор" - це саме те, що ви маєте на увазі під "узагальненим генератором".
Josh O'Brien

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

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' є об'єктом генератора, він показує 'False'. Дивіться моє запитання, я хочу зробити перевірку об’єктів генератора.
Дагад Пушпак

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) використовувати вкладений блок спробу .

Або (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. Я запропонував зміни, які б запобігли цій небажаній поведінці.
Дюни

Це більш піфонічний спосіб зробити це, якщо я не помиляюся.
JAB

Хоча, якщо ви повторюєте перелік елементів, і більшість з них не є ітераторами, ніж ітератори, то це може зайняти більше часу?
Якоб Бойер

1
@Jakob Bowyer: Винятки швидші, ніж ifзаяви. І. Така мікрооптимізація - це марна трата часу. Зафіксуйте алгоритм, який створює змішаний пакет ітераторів і неітераторів, щоб виробляти лише ітератори та врятувати себе від цього болю.
S.Lott

10
Це було б помилково вважати будь-який ітерабельний генератор.
balki

1

Якщо ви використовуєте веб-сервер tornado або подібний, ви, можливо, виявили, що серверні методи насправді є генераторами, а не методами. Це ускладнює виклик інших методів, оскільки вихід не працює всередині методу, і тому вам потрібно почати керувати пулами ланцюгових об'єктів генератора. Простий метод управління пулами ланцюгових генераторів - це створення довідкової функції, такої як

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]

Що, мабуть, ви хочете, якщо ви хочете використовувати генератори як альтернативні нитки або подібні.


1

(Я знаю, що це стара публікація.) Не потрібно імпортувати модуль, ви можете оголосити об’єкт для порівняння на початку програми:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.