Python 3.x中zip与zip_longest工作机制及结果不符预期的疑问
迭代器重复引用在zip_longest中的行为困惑
代码片段
from itertools import zip_longest list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] args = [iter(list1)] * 4 zipped = zip_longest(*args, fillvalue=None) for j in zipped: print(j)
预期输出
('a', 'a', 'a', 'a') ('b', 'b', 'b', 'b') ('c', 'c', 'c', 'c') ('d', 'd', 'd', 'd') ('e', 'e', 'e', 'e') ('f', 'f', 'f', 'f') ('g', 'g', 'g', 'g') ('h', 'h', 'h', 'h') ('i', 'i', 'i', 'i') ('j', 'j', 'j', 'j')
实际输出
('a', 'b', 'c', 'd') ('e', 'f', 'g', 'h') ('i', 'j', None, None)
困惑点
原本以为args里是同一迭代器的四次引用,每次应该取到相同的第n个元素,从而输出每个元素重复四次的元组,但实际却是按原列表顺序分组输出。想了解Python 3.x中zip和zip_longest处理可迭代对象的具体机制。
问题解析
核心原因:迭代器的状态性
迭代器是有状态的对象,每次调用next()方法时,它会返回当前指向的元素,同时将内部指针移动到下一个位置,不会回头。你写的[iter(list1)] * 4并不是创建四个独立的迭代器,而是创建了一个包含四个同一迭代器引用的列表——四个元素指向的是同一个内存里的迭代器对象。
zip_longest的工作机制
zip_longest的执行逻辑是这样的:
- 每次进入一轮循环,会依次对传入的每个可迭代对象调用
next()方法; - 把这些
next()得到的结果收集成一个元组,作为当前迭代的输出; - 重复这个过程,直到所有传入的可迭代对象都被耗尽。
放到你的代码里,具体过程是:
- 第一轮循环:依次对四个同一个迭代器调用
next(),分别得到a、b、c、d,组成元组('a', 'b', 'c', 'd'); - 第二轮循环:继续对这个迭代器调用
next(),此时指针已经在d之后,依次得到e、f、g、h,组成第二个元组; - 第三轮循环:再继续调用
next(),得到i、j,之后迭代器已经没有元素了,剩下的两个位置用fillvalue=None补充,得到('i', 'j', None, None); - 迭代器耗尽,循环结束。
zip与zip_longest的具体差异
zip:以最短的传入可迭代对象为准,只要有一个可迭代对象被耗尽,就停止迭代,不会补充缺失值;zip_longest:以最长的传入可迭代对象为准,直到所有可迭代对象都被耗尽,不足的位置用指定的fillvalue(默认是None)填充。
两者的核心逻辑一致:都是每次迭代时依次从每个可迭代对象中取一个元素,直到满足停止条件。
内容的提问来源于stack exchange,提问作者kamote0330
相关产品推荐
相关产品推荐

