python - 生成器函数中的 itertools.takewhile - 为什么只评估一次?

标签 python generator python-itertools

我有一个这样的文本文件:

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 方法时.

这个假设让我想知道如果我再次调用生成器函数会发生什么。结果是它创建了一个新的生成器对象,该对象在向我抛出 StopIteration 之前也产生了一个结果(预期的第二个结果,即 ["11"]) .所以事实上,将它写成一个生成器函数会有效地给出相同的结果,就好像我将它写成一个普通函数并returned列表而不是yielding它一样.

我想我可以通过创建自己的类来代替生成器来解决这个问题(如 John Millikin 对 this question 的回答)。但关键是我希望写一些比我原来的生成器函数(甚至可能是生成器表达式)更简洁的东西。有人可以告诉我我做错了什么,以及如何改正吗?

最佳答案

对于 groupby,您正在尝试做的是一项完美的工作:

from itertools import groupby

def read_parag(filename):
    with open(filename) as f:
        for k,g in groupby((line.strip() for line in f), bool):
            if k:
                yield list(g)

这将给出:

>>> list(read_parag('myfile.txt')
[['11', '2', '3', '4'], ['11'], ['111']]

或者在一行中:

[list(g) for k,g in groupby((line.strip() for line in open('myfile.txt')), bool) if k]

关于python - 生成器函数中的 itertools.takewhile - 为什么只评估一次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11852579/

相关文章:

python - 在 Python 中使用 pyodbc 时为 "Invalid character value for cast specification"

python - 了解fit_generator(steps_per_epoch)、validation_steps、evaluate_generator(步骤)和predict_generator(步骤)

python - 将列表拆分为所有组合

Python:生成一个包含 5 列的多值真值表,其中每列可以取一组特定的值

python - 有效地求和与另一个数组匹配的索引对应的numpy数组的元素

python - 根据另一个引用追加

java - 随 secret 码生成器返回错误字符 - java

ruby-on-rails - Ruby on Rails 生成模型字段 :type - what are the options for field:type?

python - 从 n 个整数列表(可能长度不等)中进行所有可能的 n 长度排列

python - 将多个 json 对象转换为 pandas dataframe