Подтвердить что ты не робот

Преобразует ли enumerate() объект-генератор?

Как полный новичок Python, он, безусловно, выглядит так. Запуск следующее...

x = enumerate(['fee', 'fie', 'foe'])
x.next()
# Out[1]: (0, 'fee')

list(x)
# Out[2]: [(1, 'fie'), (2, 'foe')]

list(x)
# Out[3]: []

... Я замечаю, что: (a) x имеет метод next, как кажется требуемый для генераторов, и (b) x может повторяться только один раз, a характеристика генераторов, подчеркнутых в этом знаменитом python -tag ответить.

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

import types
import inspect

x = enumerate(['fee', 'fie', 'foe'])

isinstance(x, types.GeneratorType)
# Out[4]: False

inspect.isgenerator(x)
# Out[5]: False

... а третий плохо upvoted ответ на этот вопрос, похоже, указывает, что <Т25 > действительно на самом деле возвращают генератор:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')

isgenerator(x)
# Out[8]: True

Итак, что происходит? Является ли x генератором или нет? Это в некотором смысле "генератор-подобный", но не фактический генератор? Использует ли Python duck-typing означает, что тест, описанный в последнем кодексе выше на самом деле лучший?

Вместо того, чтобы продолжать записывать возможности, проходящие через мой голова, я просто выброшу это для тех из вас, кто немедленно знать ответ.

4b9b3361

Ответ 1

В документации Python говорится, что enumerate функционально эквивалентен:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

Реальная функция enumerate возвращает итератор, но не фактический генератор. Вы можете увидеть это, если вы вызываете help(x) после создания объекта enumerate:

>>> x = enumerate([1,2])
>>> help(x)
class enumerate(object)
 |  enumerate(iterable[, start]) -> iterator for index, value of iterable
 |  
 |  Return an enumerate object.  iterable must be another object that supports
 |  iteration.  The enumerate object yields pairs containing a count (from
 |  start, which defaults to zero) and a value yielded by the iterable argument.
 |  enumerate is useful for obtaining an indexed list:
 |      (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T

В Python генераторы - это в основном конкретный тип итератора, который реализован с помощью yield для возврата данных из функции. Однако enumerate фактически реализуется на C, а не на чистом Python, поэтому нет yield. Вы можете найти источник здесь: http://hg.python.org/cpython/file/2.7/Objects/enumobject.c

Ответ 2

Тестирование типов перечисления:

Я бы включил этот важный тест в исследование типа перечисления и как он вписывается в язык Python:

>>> import collections
>>> e = enumerate('abc')
>>> isinstance(e, enumerate)
True
>>> isinstance(e, collections.Iterable)
True
>>> isinstance(e, collections.Iterator)
True

Но мы видим, что:

>>> import types
>>> isinstance(e, types.GeneratorType)
False

Итак, мы знаем, что перечисляемые объекты не являются генераторами.

Источник:

В источнике (из Python 2.7), мы видим, что объект перечисления (PyEnum_Type), который итеративно возвращает кортеж, а в ABC, мы можем видеть, что любой элемент с методом next и __iter__ (фактически, атрибут) определяется как итератор. (__next__ в Python 3.)

Стандартный тест библиотеки

Таким образом, библиотека абстрактного базового класса использует следующий тест:

>>> hasattr(e, 'next') and hasattr(e, '__iter__')
True

Итак, мы знаем, что перечисляемые типы являются итераторами. Но мы видим, что тип генератора создается функцией с доходностью в документация или выражение генератора. Таким образом, генераторы являются итераторами, потому что они имеют методы next и __iter__, но не все итераторы обязательно являются генераторами, как мы видели с этим объектом перечисления.

Итак, что мы знаем о enumerate?

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

Мы также знаем из документации, что типы генераторов просто "обеспечивают удобный способ реализации протокола итератора. "Следовательно, генераторы являются подмножеством итераторов. Кроме того, это позволяет получить следующее обобщение:

Все генераторы являются итераторами, но не все итераторы являются генераторами.

Итак, пока мы можем сделать наш объект перечисления в генератор:

>>> g = (i for i in e)
>>> isinstance(g, types.GeneratorType)
True

Мы не можем ожидать, что это сам генератор, поэтому это будет неправильный тест.

Итак, что тестировать?

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

Если вам нужен тип перечисления, вы, вероятно, захотите разрешить итерации или итераторы кортежей с целыми индексами, а следующее возвращает True:

isinstance(g, collections.Iterable)

Если вам нужен только тип перечисления:

isinstance(e, enumerate)

PS Если вам интересно, вот исходная реализация генераторов: http://hg.python.org/cpython/file/785e29d8ce13/Objects/genobject.c

Ответ 3

Является ли это в некотором смысле "генераторным", но не фактическим генератором?

Да, это так. Тебе все равно, если это утка, но только если она ходит, разговаривает и пахнет. Он также может быть генератором, не должен иметь реальной разницы.

Обычно, если вы хотите расширить функциональность, типично использовать генераторные типы вместо реальных генераторов. Например. range также похож на генератор, но также поддерживает такие вещи, как y in range(x) и len(range(x)) (xrange в python2.x).

Ответ 4

Вы можете попробовать несколько вещей, чтобы доказать себе, что это ни генератор, ни подкласс генератора:

>>> x = enumerate(["a","b","c"])
>>> type(x)
<type 'enumerate'>
>>> import types
>>> issubclass(type(x), types.GeneratorType)
False

Как указывает Дэниел, это его собственный тип enumerate. Этот тип может быть итерируемым. Генераторы также являются итерабельными. Этот второй, проголосовавший ответ, который вы указываете, в основном просто указывает, что косвенно говорит о методе __iter__.

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

Поэтому вместо того, чтобы сказать, что что-то типа enumerate является "генераторным", имеет смысл просто сказать, что классы enumerate и GeneratorType являются итерабельными (вместе со списками и т.д.). Как они перебирают данные (и форму данных, которые они хранят) могут быть совсем другими, но интерфейс один и тот же.

Надеюсь, что это поможет!

Ответ 5

enumerate генерирует объект перечисления. Это итератор, как генератор.