#python #generator #python-3.6 #stopiteration
#python #генератор #python-3.6 #остановка чтения
Вопрос:
Я только что получил бит, злоупотребляя StopIteration
некоторыми вложенными генераторами (используя CPython 3.6.9), не включил PEP 479 ( from __future__ import generator_stop
) и имел какой-то плохой хакерский код, который использовал next(iter(iterable))
это преждевременно сигнализировал об остановке.
В то время как PEP 479 будет перехвачен StopIteration
при выходе из генераторов, я думаю, что я все равно столкнусь с этим во вложенных циклах for.
На данный момент я собираюсь заменить любое использование next(iter(...))
следующим:
def take(iterable, *, n):
"""
Robustly gets the first n items from an iterable and returns them as a
list.
You should always use this function in lieu of `next(iter(...))`! e.g.
instead of:
my_first = next(iter(container))
you should instead do:
my_first, = take(container, n=1)
Throws RuntimeError if the iterable cannot yield n items.
"""
iterator = iter(iterable)
out = []
for _ in range(n):
try:
out.append(next(iterator))
except StopIteration:
raise RuntimeError("Premature StopIteration encountered!")
return out
Мой вопрос: такая функция уже есть в stdlib для Python?
Я проверил python.org
последние документы s (для 3.9) в itertools
and builtins
, и самое близкое, что я мог видеть, было takewhile
, но я об этом. Я мог бы также преобразовать в a list
или любой другой индексируемый контейнер, но я бы хотел избежать необходимости перебирать все только для доступа к первому.
Комментарии:
1.
islice
может быть?2. Вы ищете
itertools.islice
3. О, да! Похоже, это оно — спасибо!!!
Ответ №1:
itertools.islice
делает это (и многое другое) без преобразования в список или ошибок, если создается недостаточно элементов.
Вы могли бы написать свою функцию в терминах этого чисто:
def take(iterable, *, n):
li = list(itertools.islice(iterable, n))
if len(li) != n:
raise RuntimeError("too short iterable for take")
return li