是否有办法将cycle
从itertools转换为list
?应用list(my_cycle)
会冻结我的计算机。
我想定期在一组对象之间无限地切换。它们以周期存储。如果我的一个对象变为“不活动”,我想将其从循环中删除。我用另一个带有不活动对象的列表解决了它,但看起来似乎是不好的解决方法。
答案 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
此设计的优点是:
主要缺点是,它在“什么都没有改变”的情况下增加了一点点开销,特别是如果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)