为什么在 iterable 上调用 list() 会改变它?

Why does calling list() on iterable change it?

考虑这段代码,其中我使用 combinations 并尝试从中列出一个列表。

from itertools import combinations

t = (1,2,3,4)
print("t is %r" % (t,))
print("list(t) is %r" % list(t))
print("list(t) is %r" % list(t))

t2 = ("a", "b", "c", "d")
print("t2 is %r" % (t2,))

combs = combinations(t2, 2)
print("List of combinations of t2: %r" % list(combs))
print("List of combinations of t2: %r" % list(combs))

输出是(出乎我的意料)

t is (1, 2, 3, 4)
list(t) is [1, 2, 3, 4]
list(t) is [1, 2, 3, 4]
t2 is ('a', 'b', 'c', 'd')
List of combinations of t2: [('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
List of combinations of t2: []

很明显,list() 有副作用。 正如预期的那样,将元组转换为列表不会更改原始数据,我可以多次这样做。但是,当我对从 combinations 返回的可迭代对象进行同样的尝试时,这只起作用一次,然后可迭代对象似乎无效。 list 是否在 iterable 上调用 next 以便在它完成后,迭代器在最后或者为什么会发生这种情况? 我该如何避免呢?

itertools.combinations 生成惰性生成器,而不是保存在内存中的完整数据结构。一旦你用 list() 之类的东西耗尽它(遍历它),它就......好吧,耗尽了。空的。如果想重复使用,保存一个参考:

combs = list(combinations(t2, 2))
print("List of combinations of t2: %r" % combs)
print("List of combinations of t2: %r" % combs)

如您所见,list 具有破坏性,因为发电机只能耗尽一次。一个简单的解决方案是使用 itertools.tee:

>>> c1, c2 = itertools.tee(itertools.combinations(["a", "b", "c"], 2))
>>> print(list(c1))
... will print the entire sequence of combinations
>>> print(list(c2))
... same as before

这可能比保留整个列表更节省内存,因为 itertools.tee 只需要保留尚未被所有迭代器消耗的元素。