将python`cycle`转换为`list`

时间:2018-09-17 18:20:30

标签: python python-3.x list itertools

是否有办法将cycle从itertools转换为list?应用list(my_cycle)会冻结我的计算机。

我想定期在一组对象之间无限地切换。它们以周期存储。如果我的一个对象变为“不活动”,我想将其从循环中删除。我用另一个带有不活动对象的列表解决了它,但看起来似乎是不好的解决方法。

2 个答案:

答案 0 :(得分:1)

不,您不能这样做,因为cycle是无限序列。您的计算机“冻结”,因为Python试图迭代一个永无休止的项目集合(如果将其放置足够长的时间,则该过程将耗尽内存并崩溃)。

您可以做的是,将预定数量的预定物品收集到一个列表中:

n = 10  # some fixed size 
results = []
for i in range(n):
    results.append(next(my_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方法是有效的)。

答案 1 :(得分:1)

如果所有对象(活动对象和非活动对象)的集合从未改变,尤其是在活动状态和非活动状态之间的转换很常见的情况下,对象的总数就不会太离谱,或者非活动对象的集合通常不会覆盖大多数对象在整个集合中,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

此设计的优点是:

  1. 激活和取消激活对象很便宜
  2. 它无限期地保留原始迭代顺序(如果对象最初位于位置4,则当它变为非活动状态时将被跳过,而再次变为活动状态时,它将在位置3之后和位置5之前再次出现,下一次循环显示)

主要缺点是,它在“什么都没有改变”的情况下增加了一点点开销,特别是如果set的{​​{1}}增长到对象总数的相当一部分时,尤其如此;您仍然必须inactiveobjects 所有个对象,即使过滤掉很多个对象。

如果这不适合您的用例,则wim建议在cycle上构建的cycle的自定义版本可能是最佳的通用解决方案:

deque

用途为:

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)