Как выбрать только один элемент из генератора (в Python)? - PullRequest
171 голосов
/ 20 января 2011

У меня есть функция генератора, подобная следующей:

def myfunct():
  ...
  yield result

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

for r in myfunct():
  dostuff(r)

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

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

Ответы [ 5 ]

249 голосов
/ 20 января 2011

Создать генератор, используя

g = myfunct()

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

next(g)

(или g.next() в Python 2.5 или ниже).

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

next(g, default_value)
28 голосов
/ 12 февраля 2016

Для выбора только одного элемента генератора используйте 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: из 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 является обычным.

2 голосов
/ 20 января 2011

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

0 голосов
/ 20 января 2011
generator = myfunct()
while True:
   my_element = generator.next()

убедитесь, что вы получили исключение, выданное после последнего элемента

0 голосов
/ 20 января 2011

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

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