Есть ли что-то еще для enumerate (), чем просто zip (range (len ()))? - PullRequest
0 голосов
/ 29 мая 2020

Все в Python существует не просто так. Все системы на базе Python зависят от примерно 50 встроенных функций, большинство из которых чрезвычайно полезны и уникальны , например format(), len(), list() или range(). Я не могу понять, почему enumerate() существует .

Он был представлен в PEP 279 (2002) и сохранился до сих пор. Я действительно не понимаю, почему он существует, потому что это можно сделать, используя другие более важные встроенные функции, еще на 2-3 символа. Из Python Документов :

seasons = ['Spring', 'Summer', 'Fall', 'Winter']
for i in enumerate(seasons):
    print(i)

Реализация с использованием более важных встроенных функций следующая:

for i in zip(range(len(seasons)), seasons):
    print(i)
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]

Эти два одинаковы, и все мы знаем, насколько критически важны zip() и range(). Так зачем добавлять встроенную функцию, которая, казалось бы, не добавляла бы никакого значения, кроме этих двух?

В Python Документах , вот эквивалент enumerate():

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

Итог : мне интересно, обладает ли enumerate() некоторыми уникальными возможностями, которых я не вижу.

Ответы [ 3 ]

7 голосов
/ 29 мая 2020

Потому что не каждая итерация имеет длину.

>>> def countdown(x):
...     while x >= 0:
...         yield x
...         x -= 1
...         
>>> down = countdown(3)
>>> len(down)
Traceback (most recent call last):
[...]
TypeError: object of type 'generator' has no len()
>>> enum = enumerate(down)
>>> next(enum)
(0, 3)
>>> next(enum)
(1, 2)

Это, конечно, тривиальный пример. Но я мог придумать множество объектов реального мира, для которых невозможно предварительно вычислить длину. Либо потому, что длина бесконечна (см. itertools.count), либо потому, что объект, который вы повторяете, сам не знает, когда вечеринка закончилась.

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

def get_user_input():
     while True:
        i = input('input value or Q to quit: ')
        if i == 'Q':
            break
        yield i

Вы не можете получить длину get_user_input(), но можете enumerate все входные данные по мере их извлечения через next (или итерацию).

3 голосов
/ 29 мая 2020

Самая большая проблема, которую я вижу, это то, что Iterable не обязательно является конечным. len не имеет смысла для каждой структуры:

def infinite():
    n = 0
    while True:
        yield n
        n += 1

for i, n in enumerate(infinite()):
    print(i, n)

Было бы невозможно использовать вашу реализацию для перечисления infinite.

Если бы вы использовали infinite (или itertools.count) вместо range, это будет работать:

for i, n in zip(infinite(), seasons):
    print(i, n)

Но, в отличие от таких языков, как Clojure, range, к сожалению, не может быть бесконечным. Вы должны указать конец, для чего необходимо заранее знать длину, что усложняет ситуацию.

2 голосов
/ 29 мая 2020

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

from itertools import count
for i, o in zip(count(), iterable):
    ...

VS

for i, o in enumerate(iterable):
    ...

Кроме того, использование len добавляет еще одну ненужную операцию. Хотя включение enumerate устраняет необходимость импорта из библиотеки для таких простых нужд.

...