Как выбрать один элемент из генератора (в 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]