当我使用循环将两个生成器附加到列表时,第一个生成器复制第二个生成器的输出。当我展开循环时,我得到了不同的输出。
以下代码演示了此问题。
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
与原始循环代码具有相同的行为。)
答案 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]