Как выбрать один элемент из генератора (в python)?

У меня есть функция генератора, такая как:

def myfunct():
  ...
  yield result

Обычный способ вызова этой функции:

for r in myfunct():
  dostuff(r)

Мой вопрос, есть ли способ получить только один элемент из генератора всякий раз, когда мне нравится? Например, я хотел бы сделать что-то вроде:

while True:
  ...
  if something:
      my_element = pick_just_one_element(myfunct())
      dostuff(my_element)
  ...

Ответ 1

Создайте генератор с помощью

g = myfunct()

Каждый раз, когда вы хотите использовать элемент, используйте

g.next()

или

next(g)

Если генератор выйдет, он поднимет StopIteration. Вы можете либо поймать это исключение, если это необходимо, либо использовать аргумент default для next():

next(g, default_value)

Ответ 2

Для выбора только одного элемента генератора используйте break в выражении for или list(itertools.islice(gen, 1))

В соответствии с вашим примером (литерно) вы можете сделать так:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

Если вы хотите "получить только один элемент из генератора [однажды сгенерированного], когда мне нравится" (я полагаю, что 50% - это первоначальное намерение и наиболее распространенное намерение):

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

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

Раздел else: of for необходим только в том случае, если вы хотите сделать что-то особенное в случае конца генератора.

Примечание в next()/.next():

В Python3 метод .next() был переименован в .__next__() по уважительной причине: его рассмотренный низкий уровень (PEP 3114). До Python 2.6 встроенная функция next() не существовала. И даже было обсуждено, чтобы переместить next() в модуль operator (что было бы разумно) из-за его редкой необходимости и сомнительной инфляции встроенных имен.

Использование next() по умолчанию по-прежнему является очень низкоуровневой практикой - открыто бросать криптоватый StopIteration как болт из синего в обычный код приложения. И использование next() со значением по умолчанию - это лучше всего должно быть единственным вариантом для next() непосредственно в builtins - ограничено и часто дает основание для нечетной непифонической логики/удобочитаемости.

Нижняя строка: Использование next() должно быть очень редким - например, с использованием функций модуля operator. Используя for x in iterator, islice, list(iterator) и другие функции, принимающие итератор плавно, являются естественным способом использования итераторов на уровне приложений - и вполне возможно. next() является низкоуровневой, дополнительной концепцией, неочевидной - как показывает вопрос об этом потоке. Хотя, например, использование break в for является общепринятым.

Ответ 3

Я не верю, что есть удобный способ получить произвольное значение от генератора. Генератор предоставит следующий() метод для перемещения, но полная последовательность не создается немедленно для сохранения памяти. Это функциональное различие между генератором и списком.

Ответ 4

generator = myfunct()
while True:
   my_element = generator.next()

Обязательно поймайте исключение, созданное после последнего элемента.

Ответ 5

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

l = list(myfunct())
l[4]