代码之家  ›  专栏  ›  技术社区  ›  danielleontiev

将python`cycle`转换为`list`

  •  -2
  • danielleontiev  · 技术社区  · 6 年前

    cycle 从itertools到 list ? 应用 list(my_cycle) 冻结我的电脑。

    2 回复  |  直到 6 年前
        1
  •  1
  •   ShadowRanger    6 年前

    cycle 在这里仍然可以很好地工作 set

    from itertools import cycle, filterfalse
    
    allobjects = [...]
    numuniqueobjects = len(set(allobjects))
    inactiveobjects = set()
    
    # Each time we request an item, filterfalse pulls items from the cycle
    # until we find one that isn't in our inactive set
    for object in filterfalse(inactiveobjects.__contains__, cycle(allobjects)):
    
        # ... do actual stuff with object ...
    
        # Any objects that should go active again get removed from the set and will be
        # seen again the next time their turn comes up in the original order
        inactiveobjects -= objects_that_should_become_active()
    
        # Won't see this object again until it's removed from inactiveobjects
        if object.should_go_inactive():
            inactiveobjects.add(object)
            if len(inactiveobjects) == numuniqueobjects:
                # Nothing is active, continuing loop would cause infinite loop
                break
    

    这种设计的优点是:

    主要的缺点是它会给“什么都没有改变”的情况增加一点点开销,特别是如果 设置 inactiveobjects 增长到相当于对象总数的一小部分;您仍然需要 周期 对象即使过滤掉 许多的 他们中的一个。

    如果这不适合您的用例,那么 deque 根据建议 wim 可能是最好的通用解决方案:

    from collections import deque
    from collections.abc import Iterator
    
    class mutablecycle(Iterator):
        def __init__(self, it):
            self.objects = deque(it)
            self.objects.reverse() # rotate defaults to equivalent of appendleft(pop())
                                   # reverse so next item always at index -1
    
        def __next__(self):
            self.objects.rotate() # Moves rightmost element to index 0 efficiently
            try:
                return self.objects[0]
            except IndexError:
                raise StopIteration
    
        def removecurrent(self):
            # Remove last yielded element
            del self.objects[0]
    
        def remove(self, obj):
             self.objects.remove(obj)
    
        def add(self, obj, *, tofront=True):
            if tofront:
                # Putting it on right makes it be yielded on next request
                self.objects.append(obj)
            else:
                # Putting it on left makes it appear after all other elements
                self.objects.appendleft(obj)
    

    mycycle = mutablecycle(allobjects):
    for object in mycycle:
        # ... do stuff with object ...
    
        if object.should_go_inactive():
            mycycle.removecurrent()  # Implicitly removes object currently being iterated
    
        2
  •  1
  •   wim    6 年前

    不,你不能,因为 cycle 是一个无限序列。您的计算机“冻结”是因为Python试图迭代一个永无止境的项集合(如果您将其保留足够长的时间,该进程将耗尽内存并崩溃)。

    您可以做的是,将预先确定的有限数量的项目收集到一个列表中:

    n = 10  # some fixed size 
    results = []
    for i in range(n):
        results.append(next(my_cycle))
    

    没有通用的方法可以知道一次循环要消耗多少项,因为cycle对象不公开任何关于底层迭代周期的状态,即在重复之前迭代了多少项。

    没有任何公共方法可以修改从循环返回的项,只要第一个循环返回一次 StopIteration

    >>> L = [0,1,2]
    >>> g = itertools.cycle(L)
    >>> next(g)
    0
    >>> L.remove(1)
    >>> next(g)
    2
    >>> next(g)
    0
    >>> L.remove(2)
    >>> next(g)
    2
    

    collections.deque 实例作为数据结构 rotate 方法是有效的)。