itertools.take while внутри функции-генератора -почему она оценивается только один раз?

У меня есть такой текстовый файл:

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его.

Думаю, я мог бы решить эту проблему, создав собственный класс для использования вместо генератора (, как в ответе Джона Милликина на этот вопрос). Но дело в том, что я надеялся написать что-то более лаконичное, чем моя исходная функция генератора (возможно, даже выражение генератора ). Может кто-нибудь сказать мне, что я делаю неправильно, и как это исправить?

10
задан Community 23 May 2017 в 12:17
поделиться