Получить n-й элемент генератора в Python - PullRequest
49 голосов
/ 20 февраля 2010

Есть ли более синтаксически лаконичный способ написать следующее?

gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
    if i is index:
        return v

Кажется почти естественным, что генератор должен иметь выражение gen[index], которое действует как список, но функционально идентично приведенному выше коду.

Ответы [ 8 ]

51 голосов
/ 20 февраля 2010

Один метод будет использовать itertools.islice

>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5
15 голосов
/ 20 февраля 2010

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

from itertools import islice, count
next(islice(count(), n, n+1))
3 голосов
/ 23 января 2019

Я думаю, что лучший способ это:

next(x for i,x in enumerate(it) if i==n)

(где it - ваш итератор, а n - индекс)

Не требуется добавлять импорт (например, решения, использующие itertools) или загружать все элементы итератора в память одновременно (как решения, использующие list).

Примечание 1: эта версия выдает ошибку StopIteration, если в вашем итераторе меньше n элементов. Если вы хотите получить None вместо этого, вы можете использовать:

next((x for i,x in enumerate(it) if i==n), None)

Примечание 2: в вызове next нет скобок. Это не понимание списка, а понимание генератора, которое не потребляет исходного итератора дальше, чем его n-й элемент.

2 голосов
/ 31 мая 2017

Я бы поспорил против соблазна обращаться с генераторами как со списками.Простой, но наивный подход - простая однострочная:

gen = (i for i in range(10))
list(gen)[3]

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

>>> gen = (i for i in range(10))
>>> list(gen)[3]
3
>>> list(gen)[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

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

Давайте рассмотрим другой пример, основанный на коде из вопроса.

Первоначально можно ожидать, что следующее напечатает 4 дважды.

gen = (i for i in range(10))
index = 4
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)

, но напечатайте это в repl, и вы получите:

>>> gen = (i for i in range(10))
>>> index = 4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
9

Удачи в поиске этой ошибкивниз.

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

Как указывалось, если генератор бесконечно долго, вы даже не можете преобразовать его в список.Выражение list(gen) никогда не закончится.

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

1 голос
/ 06 января 2019

Лучше всего использовать: пример:

a = gen values ('a','c','d','e')

поэтому ответ будет:

a = list(a) -> this will convert the generator to a list (it will store in memory)

тогда, когда вы хотите перейти к определенному индексу, вы будете:

a[INDEX] -> and you will able to get the value its holds 

, если вы хотите знать только количество или выполнять операции, которые не требуется хранить в памяти, рекомендуется: a = sum(1 in i in a) -> это будет подсчитывать количество объектов у вас

надеюсь, я сделал это проще.

0 голосов
/ 30 января 2018

Первое, что пришло мне в голову, было:

gen = (i for i in xrange(10))
index = 5

for i, v in zip(range(index), gen): pass

return v
0 голосов
/ 14 марта 2017

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

>>> [i for i in range(10)][index]
5
0 голосов
/ 20 февраля 2010

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

>>> gen = xrange(10)
>>> ind=5 
>>> gen[ind]
5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...