不同的结果展开循环 - Python生成器

时间:2017-10-17 15:27:12

标签: python python-3.x generator

当我使用循环将两个生成器附加到列表时,第一个生成器复制第二个生成器的输出。当我展开循环时,我得到了不同的输出。

以下代码演示了此问题。

import itertools

iterators = itertools.tee(itertools.repeat(('a', 0), 5), 2)
result = []
result.append(r[0] for r in iterators[0])
result.append(r[1] for r in iterators[1])

# As expected
print('Written out...')
print(list(result[0])) # ['a', 'a', 'a', 'a', 'a']
print(list(result[1])) # [0, 0, 0, 0, 0]


# Now do it again but use a loop
iterators = itertools.tee(itertools.repeat(('a', 0), 5), 2)
result = []
for index in [0, 1]:
    result.append(r[index] for r in iterators[index])

# This time both lists are of the second item.
print('With a loop...')
print(list(result[0])) # [0, 0, 0, 0, 0] <--- Huh?!
print(list(result[1])) # [0, 0, 0, 0, 0]

为什么循环版本不能像我预期的那样工作?我该怎么办呢?

解决方案

现在关闭这个副本我不能发布另一个答案,但是对于记录,这是我最后使用的解决方案。

@MikeMüller指出的问题是索引index的{​​{1}}实例是后期绑定的。以下强制通过为循环中的r的每个值创建一个新的局部变量实例i来强制进行早期绑定:

index

(我也很喜欢Mike的建议,一直使用生成器,但不幸的是我需要外部生成器(for index, it in enumerate(iterators): g = lambda i: (r[i] for r in it) # force early binding on index result.append(g(index)) )才能实现,所以我可以重复引用result中的各个元素但是result与原始循环代码具有相同的行为。)

1 个答案:

答案 0 :(得分:2)

修复

您需要先使用迭代器:

for index in [0, 1]:
    result.append(list(r[index] for r in iterators[index]))

获得相同的效果。

现在:

print('With a loop...')
print(result[0])
print(result[1])

输出:

With a loop...
['a', 'a', 'a', 'a', 'a']
[0, 0, 0, 0, 0]

在循环之后说明此集index = 0

iterators = itertools.tee(itertools.repeat(('a', 0), 5), 2)
result = []
for index in [0, 1]:
    result.append(r[index] for r in iterators[index])
index = 0
print('With a loop...')
print(list(result[0]))
print(list(result[1]))

现在tee的第一部分被使用了两次,因为r[index]总是meànsr[0]

With a loop...
['a', 'a', 'a', 'a', 'a']
['a', 'a', 'a', 'a', 'a']

原因

index懒惰地应用,即实际转换为列表时。 由于索引在循环之后为1,因此它在1中使用此r[index]两次,并且您在迭代器中获得第二项两次。

替代

一直使用迭代器直到消耗:

iterators = itertools.tee(itertools.repeat(('a', 0), 5), 2)
result = ((r[index] for r in iterators[index]) for index in [0, 1])
for res in result:
    print(list(res))

输出:

['a', 'a', 'a', 'a', 'a']
[0, 0, 0, 0, 0]