Python zip():检查哪个迭代已耗尽

问题描述

在 Python 3 中,zip(*iterables)documentation

返回一个元组迭代器,其中第 i 个元组包含来自每个参数序列或可迭代对象的第 i 个元素。当最短的输入迭代用完时,迭代器停止。

举个例子,我正在跑步

for x in zip(a,b):
  f(x)

有没有办法找出哪些可迭代对象 ab 导致 zip 迭代器停止?

假设 len() 不可靠并且迭代 ab 以检查它们的长度是不可行的。

解决方法

我认为简给出了最好的答案。基本上,您希望单独处理 zip 中的最后一次迭代。

import itertools as it

a = (x for x in range(5))
b = (x for x in range(3))

iterables = ((it.chain(g,[f"generator {i} was exhausted"]) for i,g in enumerate([a,b])))

for i,j in zip(*iterables):
    print(i,j)

# 0 0
# 1 1
# 2 2
# 3 generator 1 was exhausted
,

如果你只有两个可迭代对象,你可以使用下面的代码。 The exhausted[0] 将拥有迭代器耗尽的指标。 None 的值表示两者都已耗尽。

但是我必须说我不同意 len() 不可靠。事实上,您应该依靠 len() 调用来确定答案。 (除非你告诉我们你不能的原因。)

def f(val):
    print(val)

def manual_iter(a,b,exhausted):
    iters = [iter(it) for it in [a,b]]
    iter_map = {}
    iter_map[iters[0]] = 'first'
    iter_map[iters[1]] = 'second'

    while 1:
        values = []
        for i,it in enumerate(iters):
            try:
                value = next(it)
            except StopIteration:
                if i == 0:
                    try:
                        next(iters[1])
                    except StopIteration:
                        return None
                exhausted.append(iter_map[it])
                return iter_map[it]
            values.append(value)
        yield tuple(values)

if __name__ == '__main__':
    exhausted = []
    a = [1,2,3]
    b = [10,20,30]
    for x in manual_iter(a,exhausted):
        f(x)
    print(exhausted)

    exhausted = []
    a = [1,3,4]
    b = [10,30,40]
    for x in manual_iter(a,exhausted):
        f(x)
    print(exhausted)            
,

我找到了以下解决方案,该解决方案将 zip 替换为 for 循环,仅覆盖第一个可迭代对象,并在循环内迭代第二个。

ib = iter(b)

for r in a:
    try:
        s = next(ib)
    except StopIteration:
        print('Only b exhausted.')
        break
    print((r,s))
else: 
    try:
        s = next(ib)
        print('Only a exhausted.')
    except StopIteration:
        print('a and b exhausted.')

这里 ib = iter(b) 确保它在 b 是序列或生成器对象时也能工作。 print((r,s)) 将替换为问题中的 f(x)