У меня есть текстовый файл:
11
2
3
4
11
111
Используя Python 2.7, я хочу превратить его в список списков строк, где разрывы строк делят элементы во внутреннем списке, а пустые строки делят элементы во внешнем списке. Например:
[["11","2","3","4"],["11"],["111"]]
И для этой цели я написал генераторную функцию, которая давала бы внутренние списки по одному за один раз, когда передал открытый файловый объект:
def readParag(fileObj):
currentParag = []
for line in fileObj:
stripped = line.rstrip()
if len(stripped) > 0: currentParag.append(stripped)
elif len(currentParag) > 0:
yield currentParag
currentParag = []
Это прекрасно работает, и я могу назвать это из понимания списка, создавая желаемый результат. Однако впоследствии мне пришло в голову, что я смогу сделать то же самое более кратко с помощью itertools.takewhile
(с целью переписать генераторную функцию как выражение генератора, но мы оставим это на данный момент). Это то, что я пробовал:
from itertools import takewhile
def readParag(fileObj):
yield [ln.rstrip() for ln in takewhile(lambda line: line != "\n", fileObj)]
В этом случае полученный генератор дает только один результат (ожидаемый первый, т.е. ["11","2","3","4"]
). Я надеялся, что вызов метода next
снова заставит его снова оценить takewhile(lambda line: line != "\n", fileObj)
в остальной части файла, что приведет к тому, что он даст другой список. Но нет: вместо этого я получил StopIteration
. Поэтому я предположил, что выражение take while
оценивалось один раз только в то время, когда был создан объект-генератор, а не каждый раз, когда я вызывал метод результирующего объекта-генератора next
.
Это предположение заставило меня задаться вопросом, что произойдет, если я снова позвоню функции генератора. В результате он создал новый объект-генератор, который также дал один результат (ожидаемый второй, т.е. ["11"]
), прежде чем набросить StopIteration
на меня. Таким образом, на самом деле написать это как функцию-генератор дает тот же результат, что и если бы я написал его как обычную функцию, а return
вместо списка yield
.
Я думаю, я мог бы решить эту проблему, создав свой собственный класс вместо генератора (как в ответ Джона Милликина на этот вопрос). Но дело в том, что я надеялся написать что-то более сжатое, чем моя исходная функция генератора (возможно, даже выражение генератора). Может кто-нибудь сказать мне, что я делаю неправильно, и как правильно это сделать?