Как получить n следующих значений генератора в списке (python)

Я сделал генератор для чтения файла словом, и он работает хорошо.

def word_reader(file):
    for line in open(file):
        for p in line.split():
            yield p

reader = word_reader('txtfile')
next(reader)

Каков самый простой способ получить n следующих значений в списке?

Ответ 1

Используйте itertools.islice:

list(itertools.islice(it, n))

Ответ 2

EDIT: используйте itertools.islice. Образец ниже, который я изначально предложил, - плохая идея - он падает, когда it дает меньше n значений, и это поведение зависит от тонких проблем, поэтому люди, читающие такой код, вряд ли поймут его точную семантику.

Есть также

[next(it) for _ in range(n)]

которые могут (?) быть более ясными для людей, не знакомых с itertools; но если вы много разбираетесь с итераторами, itertools - достойное дополнение к вашему набору инструментов.

Что произойдет, если next(it) был исчерпан и вызывает StopIteration?

(т.е. когда у it было меньше n значений)

Когда я написал вышеприведенную строку пару лет назад, я, вероятно, думал, что StopIteration будет иметь умный побочный эффект, чтобы чисто прекратить понимание списка. Но нет, все понимание StopIteration крах, проходя мимо StopIteration вверх. (Он выйдет чисто, только если исключение возникло из итератора range(n).)

Скорее всего, это не то поведение, которое вы хотите.

Но становится все хуже. Следующее должно быть эквивалентно пониманию списка (особенно на Python 3):

list(next(it) for _ in range(n))

Это не так. Внутренняя часть является сокращением для функции генератора; list() знает, что это сделано, когда он StopIteration где угодно.
=> Эта версия безопасно справляется, когда нет n значений и возвращает более короткий список. (Как itertools.islice().)

[Исполнения: 2.7, 3.4 ]

Но это тоже изменится! Тот факт, что генератор молча выходит, когда какой-либо код внутри него вызывает StopIteration является известной бородавкой, адресованной PEP 479. От Python 3.7 (или 3.5 с будущим импортом), который приведет к RuntimeError вместо RuntimeError обработки генератора. Т.е. он станет похож на поведение понимания списка. (Проверено на недавней сборке HEAD)

Ответ 3

for word, i in zip(word_reader(file), xrange(n)):
    ...

Ответ 4

Чтобы получить первые n значений генератора, вы можете использовать more_itertools.take.

Если вы планируете перебирать слова в кусках (например, 100 за раз), вы можете использовать more_itertools.chunked(https://more-itertools.readthedocs.io/en/latest/api.html):

import more_itertools
for words in more_itertools.chunked(reader, n=100):
    # process 100 words