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

Получить n-й элемент генератора в Python

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

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

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

4b9b3361

Ответ 1

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

>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5

Ответ 2

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

from itertools import islice, count
next(islice(count(), n, n+1))

Ответ 3

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

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) никогда не завершится.

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

Ответ 4

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

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 я in a) → это будет подсчитывать количество объектов, которые у вас есть

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

Ответ 5

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

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

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

return v

Ответ 6

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

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-й элемент.

Ответ 7

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

>>> gen = xrange(10)
>>> ind=5 
>>> gen[ind]
5

Ответ 8

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

>>> [i for i in range(10)][index]
5