【问题标题】:Transform python `cycle` to `list`将 python `cycle` 转换为 `list`
【发布时间】:2019-02-21 17:27:13
【问题描述】:

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

我想定期在一组对象之间无限切换。它们存储在一个循环中。如果我的一个对象变为“非活动”,我想将其从循环中删除。我用另一个包含非活动对象的列表解决了这个问题,但它看起来像是不好的解决方法。

【问题讨论】:

  • Applying list() freezes my computer. 当然可以。 cycle 从不提高 StopIteration。即使有,该列表会是什么样子?
  • 您希望得到什么结果? cycle 表示无限数量的项目一遍又一遍地重复。
  • 我想它的内部设计是基于list。所以很容易将其转换为列表
  • @danielleontiev 是什么让你这么认为?
  • @danielleontiev:您可能想就该实际问题提出一个(新)问题。你在这里有an XY problem,你试图让我们帮助让一个糟糕的方法发挥作用,而真正的解决方案是找到一个更好的方法。

标签: python python-3.x list itertools


【解决方案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 之前)

主要的缺点是它在“什么都没有改变”的情况下增加了一点点开销,尤其是当inactiveobjectsset 增长到对象总数的相当一部分时;即使过滤掉许多个对象,你仍然需要cycle 所有个对象。

如果这不适合您的用例,wim 建议的基于deque 的自定义版本的cycle 可能是最好的通用解决方案:

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】:

    不,你不能,因为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 方法很有效)。

    【讨论】:

    • @DeepSpace: 可能是为了使用itertools.islice
    • "一旦遇到来自原始迭代器的第一个 StopIteration,就没有公开的方法来修改从循环返回的项目。"除此之外,即使在原始迭代器用尽之前,任何试图通过修改原始迭代器所基于的东西来修改循环的代码都将是等待发生的维护噩梦,并且可能会破坏各种规则(隐式或显式) Python(例如,在迭代集合时不要修改集合)。
    • 重要的是要明白,在cycle 对整个可迭代对象进行一次迭代后,这将不起作用。看到这个gist 这是因为cycle 使用内部结构来存储我们无权访问的“已访问”元素。
    • 是的,实现我自己的循环或者使用像deque这样的其他数据结构是解决这个问题的最佳选择
    猜你喜欢
    • 1970-01-01
    • 2023-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 2011-11-25
    相关资源
    最近更新 更多