我有一个这样的文本文件:
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
方法。
这个假设让我想知道如果我再次调用生成器函数会发生什么。结果是它创建了一个新的生成器对象,该对象也产生了一个结果(预期的第二个结果,即 ie ["11"]
),然后向我抛出了一个StopIteration
回击。所以事实上,把它写成一个生成器函数可以有效地给出相同的结果,就好像我把它写成一个普通函数并return
ed 列表而不是yield
ing 它一样。
我想我可以通过创建自己的类而不是生成器来解决这个问题(如 John Millikin 对这个问题的回答)。但关键是我希望写出比我原来的生成器函数(甚至可能是生成器表达式)更简洁的东西。有人可以告诉我我做错了什么,以及如何做对吗?