python - 生成器和列表返回不同的结果

标签 python python-3.x generator python-itertools

我正在尝试采用一个生成器,其中每个元素都是一个列表/元组/可迭代,它们具有相同的长度,并为元素的每个索引返回一个单独的生成器。

当我对下面的 split_feat2 中的索引进行硬编码时,它会按预期工作。但是,当我使用列表理解或附加到列表并返回时,它会产生不正确的结果。

我检查了我的逻辑,并尝试通过在理解中用 () 替换 [] 来返回列表列表而不是生成器列表,它产生了正确的结果所以我不知道问题出在哪里。

任何有关其行为原因的见解都将不胜感激。

def split_feat2(gen):
    G = tee(gen, 2)
    return [(e[0] for e in G[0]), (e[1] for e in G[1])]

def split_feat(gen, n):
    G = tee(gen, n)
    return [(e[n] for e in g) for n, g in enumerate(G)]

def split_featlist(gen, n):
    G = tee(gen, n)
    return [[e[n] for e in g] for n, g in enumerate(G)]

test = lambda:((i^2,j+i) for i, j in enumerate(range(10)))

print("This is what I want")
t = split_feat2(test())
print(list(t[0]))
print(list(t[1]))
print(t)

print("\nBut I get this output")
t = split_feat(test(), 2)
print(list(t[0]))
print(list(t[1]))
print(t)

print("\nWhen I want this output but from generators instead of lists")
t = split_featlist(test(), 2)
print(list(t[0]))
print(list(t[1]))
print(t)

上面的代码输出如下:

This is what I want
[2, 3, 0, 1, 6, 7, 4, 5, 10, 11]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[<generator object split_feat2.<locals>.<genexpr> at 0x00000219C794F7D8>, <generator object split_feat2.<locals>.<genexpr> at 0x00000219C794F200>]

But I get this output
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[<generator object split_feat.<locals>.<listcomp>.<genexpr> at 0x00000219C791DB48>, <generator object split_feat.<locals>.<listcomp>.<genexpr> at 0x00000219C794F150>]

When I want this output but from generators instead of lists
[2, 3, 0, 1, 6, 7, 4, 5, 10, 11]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[[2, 3, 0, 1, 6, 7, 4, 5, 10, 11], [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]]

最佳答案

问题在于,在您实际使用生成器之前,n 变量已发生更改。因此,当函数返回生成器列表时,它位于 n - 1(函数参数 n)。因此,在您的示例中,两个生成器使用相同的索引:1。要理解我的意思,请看这个简单的例子:

>>> list_of_list = [[0, 1]]*20
>>> index = 1
>>> gen = (item[index] for item in list_of_list)
>>> print(next(gen))
1
>>> index = 0
>>> print(next(gen))  # changing index "changed the generator"
0

在您的情况下,循环不断改变n(不像我的示例中那样手动干预),但是当执行生成器时,它对于所有创建的生成器来说都是固定的,具有相同的值。

解决方案

您需要在每次迭代中以某种方式“修复”n 的当前值。一种可能是使用 mapoperator.itemgetter:

def split_feat(gen, n):
    G = tee(gen, n)
    return [map(itemgetter(n), g) for n, g in enumerate(G)]

itemgetter 立即使用“当前”n 值创建,因此结果将符合预期。

这并不是实现预期结果的唯一方法。您还可以使用创建生成器的函数。该函数将“记住”当前的n(就像一个闭包)并且也像您期望的那样工作:

def split_feat(gen, n):
    G = tee(gen, n)
    def create_generator(it, n):
        return (item[n] for item in it)
    return [create_generator(g, n) for n, g in enumerate(G)]

关于python - 生成器和列表返回不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43771832/

相关文章:

python - 如何使用python mysqldb一次插入多行

Javascript - 使用生成器而不是 promise

javascript - 如何返回到随机文本生成器函数的开头?

Python - 生成器函数在调用之间重置?

python - python中的redis

python - 是否有内置的 python 方法使用索引值而不是元素遍历列表

python - 要列出的 Pandas 列名称

python-3.x - 如何从h2标签获取HREF? Python/ Selenium

Python-是否可以一次打开多个 Chrome 网页?

python - QStyledItemDelegate 在QTableView中显示QComboBox