Як перевірити, чи об’єкт є об’єктом генератора в 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.