【问题标题】:Python zip(): Check which iterable got exhaustedPython zip():检查哪个迭代器用尽了
【发布时间】:2021-06-19 02:03:22
【问题描述】:

在 Python 3 中,zip(*iterables)documentation 一样

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

举个例子,我在跑

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

有没有办法找出导致 zip 迭代器停止的迭代器,ab

假设len() 不可靠,并且遍历ab 来检查它们的长度是不可行的。

【问题讨论】:

  • 您打算如何处理结果?这可能是XY problem。例如,您最好使用itertools.zip_longest 而不是zip
  • 顺便说一句,欢迎来到 SO!如果需要提示,请查看 tourHow to Ask
  • @wjandrea 谢谢!我的目的是确保两个迭代器中的第一个是耗尽的,否则引发异常。一个潜在的解决方法可能是仅迭代 a 并在循环内获取 next(b),但我不确定这是否完全相同,以防 a 更短。

标签: python python-3.x iterator generator


【解决方案1】:

我认为 Jan 给出了最好的答案。基本上,您想分别处理来自 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

【讨论】:

    【解决方案2】:

    如果你只有两个迭代器,你可以使用下面的代码。 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,b, exhausted):
            f(x)
        print(exhausted)
    
        exhausted = []
        a = [1,2,3,4]
        b = [10,20,30]
        for x in manual_iter(a,b, exhausted):
            f(x)
        print(exhausted)
    
        exhausted = []
        a = [1,2,3]
        b = [10,20,30,40]
        for x in manual_iter(a,b, exhausted):
            f(x)
        print(exhausted)            
    

    【讨论】:

      【解决方案3】:

      我找到了以下解决方案,该解决方案将 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) 替换。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-27
        • 2015-12-05
        • 1970-01-01
        • 1970-01-01
        • 2018-05-18
        • 1970-01-01
        • 2023-03-22
        • 2016-04-09
        相关资源
        最近更新 更多