Есть ли встроенная функция `take (iterable, n)` в Python3?

#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