【问题标题】:cycle through multiple list using itertools.cycle()使用 itertools.cycle() 循环遍历多个列表
【发布时间】:2015-03-24 06:37:29
【问题描述】:

我有一个服务器列表。每个服务器上都有一个名称列表。 示例:

server1 = ['a','b','c']
server2 = ['d','e','f']
server3 = ['g','h','i']

我想迭代每个服务器名称而不是每个服务器。例如在server1中选择'a'后,移动到'd'(不是'b')等等。如果我要使用itertools.cycle(),我是否必须创建一个服务器列表才能循环通过?我的预期结果是['a','d','g','b','e','h','c','f','i']。你能给我一个关于如何在多个列表中循环的简单例子吗?

【问题讨论】:

    标签: python itertools


    【解决方案1】:

    我们也可以使用itertools.chain.from_iterable(),比较快。

    import itertools
    
    server1 = ['a','b','c']
    server2 = ['d','e','f']
    server3 = ['g','h','i']
    
    print list(itertools.chain.from_iterable(zip(server1,server2,server3)))
    

    结果:

    ['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']
    

    【讨论】:

    【解决方案2】:

    您可以使用zipreduce 内置函数(以及在python3 中functools.reduce)来做到这一点:

    >>> list_of_servers=[server1,server2,server3]
    >>> s=reduce(lambda x,y:x+y,zip(*list_of_servers))
    >>> s
    ('a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i')
    

    或者,对于长列表,您可以使用 itertools.chain 来连接返回生成器的子列表,而不是 reduce()

    >>> list(chain(*zip(*[server1,server2,server3])))
    ['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']
    

    注意如果你想迭代你的结果,你不必在chain的结果上使用list。你可以这样做:

    for element in chain(*zip(*[server1,server2,server3])):
         #do stuff
    

    对前面的食谱进行基准测试:

    #reduce()
    :~$ python -m timeit "server1 = ['a','b','c'];server2 = ['d','e','f'];server3 = ['g','h','i'];reduce(lambda x,y:x+y,zip(*[server1,server2,server3]))"
    1000000 loops, best of 3: 1.11 usec per loop
    #itertools.chain()
    :~$ python -m timeit "server1 = ['a','b','c'];server2 = ['d','e','f'];server3 = ['g','h','i'];from itertools import chain;chain(*zip(*[server1,server2,server3]))"
    100000 loops, best of 3: 2.02 usec per loop
    

    请注意,如果您不将服务器放在列表中,它会更快:

    :~$ python -m timeit "server1 = ['a','b','c'];server2 = ['d','e','f'];server3 = ['g','h','i'];reduce(lambda x,y:x+y,zip(server1,server2,server3))"
    1000000 loops, best of 3: 0.98 usec per loop
    

    【讨论】:

    • @Shashank 是的,但是链适合长列表和像 reduce 这样的短列表更好!查看基准测试!
    • @Shashank 并感谢您注意到它没有列表更快!作为一个容器;)
    • 我喜欢downvoter关心评论并告诉我们他的原因!
    • 另外说明:在 Python 3 中,reduce 已移至 functools 模块。
    • @Kasra,reduce 看起来更快,因为您在计算中包括了导入链所需的时间。使用python -mtimeit -s "from itertools import chain" "server1 = ['a','b','c']; server2 = ['d','e','f']; server3 = ['g','h','i']; chain(*zip(server1, server2, server3));" 将导入移出定时块给了我 0.889 微秒的链,而你的减少版本在我的机器上需要 1.24 微秒
    【解决方案3】:

    这个没问题:

    >>> from itertools import chain, islice, izip, cycle
    >>> list(islice(cycle(chain.from_iterable(izip(server1, server2, server3))), 0, 18))
    ['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i', 'a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']
    

    注意,listislice 仅用于演示目的,以提供显示内容并防止无限输出...

    现在,如果您有不等长的列表,它会变得更有趣。那么izip_longest 将成为你的朋友,但此时它可能值得一个函数:

    import itertools
    def cycle_through_servers(*server_lists):
        zipped = itertools.izip_longest(*server_lists, fillvalue=None)
        chained = itertools.chain.from_iterable(zipped)
        return itertools.cycle(s for s in chained if s is not None)
    

    演示:

    >>> from itertools import islice
    >>> server3 = ['g', 'h', 'i', 'j']
    >>> list(islice(cycle_through_servers(server1, server2, server3), 0, 20))
    ['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i', 'j', 'a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i', 'j']
    

    【讨论】:

    • 如果您有未定长的列表,请查看@Anonymous's solution。如果列表长度的差异很大,则速度要快得多
    【解决方案4】:

    standard library documentationitertools 中提供此功能作为配方。

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

    即使可迭代的长度不均匀,此代码也可以工作,当较短的迭代用完时,循环遍历剩余的迭代。这可能与您的用例相关,也可能不相关。

    【讨论】:

      【解决方案5】:

      试试这个:

      from itertools import cycle
      for k in cycle([j for i in zip(server1,server2,server3) for j in i]):
         print(k)
         #do you operations
      
      a
      d
      g
      b
      ...
      

      但是关心这提供了无限循环

      所以最好这样做:

      c = cycle([j for i in zip(server1,server2,server3) for j in i])
      
      >>>next(c)
      a
      >>>next(c)
      b
      ....
      

      【讨论】:

        【解决方案6】:
        from itertools import chain
        for s in chain(*zip(server1, server2, server3)):
            # do work
        

        【讨论】:

        • 我是chain(* map(lambda *args: args, *(server1, server2, server3)),但我更喜欢你和@Anonymous 的答案
        【解决方案7】:

        你可以使用链:

        import itertools
        
        server1 = ['a','b','c']
        server2 = ['d','e','f']
        server3 = ['g','h','i']
        
        
        all_servers = [server1, server2, server3] 
        
        out_list = [s_name for a in itertools.chain(zip(*all_servers)) for s_name in a]
        
        print(out_list)
        #['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']
        

        或更短:

        out_list = list(itertools.chain.from_iterable(zip(*all_servers)))
        

        【讨论】:

        • list(itertools.chain.from_iterable(zip(*all_servers))) 不是比嵌套的 list-comp 更干净吗?
        • @mgilson 是的,但是这样的答案已经存在,所以我不想复制它。它的替代品。不过我现在也加了。
        【解决方案8】:

        使用chain,您可以简单地做到这一点:

        from itertools import chain, izip
        server1 = [1, 2]
        server2 = [3, 4]
        server3 = [4, 5]
        print list(chain(*izip(server1, server2, server3))) # [1, 3, 4, 2, 4, 5]
        


        或者您可以使用chain.from_iterable,它需要一个自身生成迭代器的可迭代对象。

        在您的情况下,zip 是可迭代的,它以元组的形式生成迭代器:

        print list(chain.from_iterable(zip(server1, server2, server3))) # [1, 3, 4, 2, 4, 5]
        


        yield 也可以在这里使用:

        def f():
            server1 = [1, 2]
            server2 = [3, 4]
            server3 = [4, 5]
            for a, b, c in zip(server1, server2, server3):
                yield a
                yield b
                yield c
        
        val = f()
        print [val.next() for _ in range(6)] # [1, 3, 4, 2, 4, 5]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-13
          • 2011-05-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-07
          • 2013-06-22
          相关资源
          最近更新 更多