【问题标题】:Create iterator to return elements from each iterable one by one创建迭代器以从每个可迭代对象中一一返回元素
【发布时间】:2019-06-23 02:31:27
【问题描述】:

我正在学习 itertools 模块,我正在尝试制作一个迭代器以从作为输入提供的可迭代对象中返回每个元素。

Agruments   Results
p, q, …     p0, q0, … plast, qlast 

如果说列表长度不同,那么next(it) 应该在较短的列表用完时返回较长列表中的元素。

尝试解决方案

import itertools
l1=[1,2,3,4,5,6]
l2=['a','b','c','d']
l=[]
for x,y in itertools.zip_longest(l1,l2):
    l.extend([x,y])
it=iter(x for x in l if x is not None)

哪种方法能解决我的问题

print(list(it))

输出:

[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 6]

有没有更简单或更好的方法来做到这一点?我在 SO 上搜索了一个解决方案,但没有找到。

【问题讨论】:

  • itertools 文档在 Recipes 部分中包含此实现,称为 roundrobin

标签: python python-3.x itertools


【解决方案1】:

您可以使用itertools.chain.from_iterable() 来展平序列,并使用生成器表达式过滤掉None 值:

from itertools import chain, zip_longest

it = (v for v in chain.from_iterable(zip_longest(l1, l2)) if v is not None)

与其使用None 作为标记值,​​不如使用专用标记,以便在输入列表中使用None

_sentinel = object()
flattened = chain.from_iterable(zip_longest(l1, l2, fillvalue=_sentinel))
it = (v for v in flattened if v is not _sentinel)

如果你想过滤掉falsey values,那么你也可以使用filter(None, ...)

it = filter(None, chain.from_iterable(zip_longest(l1, l2)))

演示:

>>> from itertools import chain, zip_longest
>>> l1 = [1, 2, 3, 4, 5, 6]
>>> l2 = ['a', 'b', 'c', 'd']
>>> it = (v for v in chain.from_iterable(zip_longest(l1, l2)) if v is not None)
>>> list(it)
[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 6]

还有当地的哨兵:

>>> l1 = [1, None, 2, None, 3, None]
>>> l2 = ['a', 'b', 'c', 'd']
>>> _sentinel = object()
>>> flattened = chain.from_iterable(zip_longest(l1, l2, fillvalue=_sentinel))
>>> it = (v for v in flattened if v is not _sentinel)
>>> list(it)
[1, 'a', None, 'b', 2, 'c', None, 'd', 3, None]

itertools recipes section 也有:

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while num_active:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            # Remove the iterator we just exhausted from the cycle.
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

【讨论】:

    【解决方案2】:

    如果您想要代码的修改版本,从一开始就构建生成器(没有存储列表l):

    import itertools
    l1=[1,2,3,4,5,6]
    l2=['a','b','c','d']
    
    def flat_zip(l1,l2):
        for x,y in itertools.zip_longest(l1,l2):
            if x:
                yield x
            if y:
                yield y
    it=flat_zip(l1,l2)
    

    虽然我建议使用上面的内置解决方案。

    【讨论】:

      【解决方案3】:

      仅使用列表推导。 cmets 中的解释。 仅为变化添加。

      A = [1, 2, 3, 4, 5, 6]
      B = ["A", "B", "C"]
      
      # zipping equal length 
      flattened_zip_list = [item for sublist in zip(A, B) for item in sublist]
      
      print(flattened_zip_list )
      
      # leftover from longest list 
      extra_list = [A[index] for index in range(min(len(A), len(B)), max(len(A), len(B)) if len(A) > len(B) else B[index])]
      
      print(extra_list)
      
      # final list 
      flattened_zip_list.extend(extra_list)
      
      print(flattened_zip_list)
      

      输出

      [1, 'A', 2, 'B', 3, 'C'] # after zipping same lengths 
      [4, 5, 6] # leftover values 
      [1, 'A', 2, 'B', 3, 'C', 4, 5, 6] # [1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 6], the one mentioned in question 
      
      [Program finished] 
      

      【讨论】:

        猜你喜欢
        • 2021-10-15
        • 2016-07-24
        • 1970-01-01
        • 1970-01-01
        • 2021-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-19
        相关资源
        最近更新 更多