【问题标题】:Circular list iterator in PythonPython中的循环列表迭代器
【发布时间】:2021-04-11 06:59:16
【问题描述】:

我需要遍历一个循环列表,可能多次,每次都从最后访问的项目开始。

用例是一个连接池。客户端请求连接,迭代器检查指向的连接是否可用并返回,否则循环直到找到可用的连接。

有没有一种简洁的方法可以在 Python 中做到这一点?

【问题讨论】:

    标签: python list iterator


    【解决方案1】:

    正确答案是使用itertools.cycle。但是,让我们假设库函数不存在。你将如何实现它?

    使用generator

    def circular():
        while True:
            for connection in ['a', 'b', 'c']:
                yield connection
    

    然后,您可以使用for 语句进行无限迭代,也可以调用next() 从生成器迭代器中获取单个下一个值:

    connections = circular()
    next(connections) # 'a'
    next(connections) # 'b'
    next(connections) # 'c'
    next(connections) # 'a'
    next(connections) # 'b'
    next(connections) # 'c'
    next(connections) # 'a'
    #....
    

    【讨论】:

    • 不错!当列表用完时它如何知道重新开始?
    • @user443854 while True 表示永远重复
    • @juanchopanza:是的; itertools.cycle 是一个更好的答案。这显示了如果 itertools 不可用,您如何编写相同的功能:)
    • 简单生成器是否也像itertools.cycle 一样保存每个元素的副本?或者简单的生成器会是更节省内存的设计吗?根据cycle docsNote, this member of the toolkit may require significant auxiliary storage (depending on the length of the iterable).
    • @dthor 这个生成器创建一个包含三个元素的列表并对其进行读写,然后永久销毁该列表并创建一个新列表。 cycle 的文档暗示输入可迭代在其生成器启动之前转换为 list,因为 iterable 仅“适用于通过一组值”。
    【解决方案2】:

    使用itertools.cycle,这正是它的目的:

    from itertools import cycle
    
    lst = ['a', 'b', 'c']
    
    pool = cycle(lst)
    
    for item in pool:
        print item,
    

    输出:

    a b c a b c ...
    

    (显然永远循环)


    为了手动推进迭代器并从中提取值,只需调用next(pool)

    >>> next(pool)
    'a'
    >>> next(pool)
    'b'
    

    【讨论】:

    • 您正在循环打印项目。我想离开循环并稍后回来? (我想从我离开的地方开始)。
    • @user443854 使用pool.next() 从循环中获取单个下一项
    • @user443854 FWIW 这是一个比我更好的答案。没有理由去重新实现库函数!
    • pool.next() 对我不起作用,只有 next(pool)。可能是因为 Python 3?
    • @fjsj 是正确的,在 Python 3 上您需要使用 next(iterator) (顺便说一句,它在 Python 2.x 上也可以正常工作,因此是应该使用的规范形式)。有关更深入的解释,请参阅Is generator.next() visible in python 3.0?。相应地更新了我的答案。
    【解决方案3】:

    您需要一个自定义迭代器——我将从this answer 改编迭代器。

    from itertools import cycle
    
    class ConnectionPool():
        def __init__(self, ...):
            # whatever is appropriate here to initilize
            # your data
            self.pool = cycle([blah, blah, etc])
        def __iter__(self):
            return self
        def __next__(self):
            for connection in self.pool:
                if connection.is_available:  # or however you spell it
                    return connection
    

    【讨论】:

      【解决方案4】:

      或者你可以这样做:

      conn = ['a', 'b', 'c', 'd', 'e', 'f']
      conn_len = len(conn)
      index = 0
      while True:
          print(conn[index])
          index = (index + 1) % conn_len
      

      永远打印 a b c d e f a b c...

      【讨论】:

        【解决方案5】:

        您可以使用append(pop()) 循环来完成此操作:

        l = ['a','b','c','d']
        while True:
            print l[0]
            l.append(l.pop(0))
        

        for i in range()循环:

        l = ['a','b','c','d']
        ll = len(l)
        while True:
            for i in range(ll):
               print l[i]
        

        或者简单地说:

        l = ['a','b','c','d']
        
        while True:
            for i in l:
               print i
        

        全部打印出来:

        >>>
        a
        b
        c
        d
        a
        b
        c
        d
        ...etc.
        

        在这三个中,我倾向于将 append(pop()) 方法作为函数

        servers = ['a','b','c','d']
        
        def rotate_servers(servers):
            servers.append(servers.pop(0))
            return servers
        
        while True:
            servers = rotate_servers(servers)
            print servers[0]
        

        【讨论】:

        • 赞成这一点,因为它帮助我完成了一个完全不同的用例,我只想多次迭代列表,每次都让开始元素前进一步。我的用例是在扑克游戏中迭代玩家,让庄家每轮将一名玩家向前推进。
        【解决方案6】:

        如果您希望循环 n 次,请实现 ncycles itertools recipe

        from itertools import chain, repeat
        
        
        def ncycles(iterable, n):
            "Returns the sequence elements n times"
            return chain.from_iterable(repeat(tuple(iterable), n))
        
        
        list(ncycles(["a", "b", "c"], 3))
        # ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
        

        【讨论】:

          【解决方案7】:

          为了避免无限循环,我使用数组长度进行迭代,直到列表大小为两倍。您可以实现自己的前置条件。想法是避免无限循环。

          #Implement Circular Linked List
          from itertools import cycle
          list=[1,2,3,4,5]
          lstlength=len(list)*2
          print(lstlength)
          pool=cycle(list)
          i=0
          #To avoid infinite loop break when you have iterated twice size of the list
          for items in pool:
              print(items)
              if i >lstlength:
                  break
              i += 1
          

          【讨论】:

            猜你喜欢
            • 2016-01-02
            • 1970-01-01
            • 1970-01-01
            • 2021-07-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-09-22
            相关资源
            最近更新 更多