无穷无尽的迭代器(无需返回副本)

时间:2018-03-23 16:12:51

标签: python iterator itertools

正常的可迭代类存在可能耗尽的问题。我遇到了不同类型的解决方案,但每个解决方案都有不同的问题。这是一个简单的例子:

class MyIter:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        self.n /= 2
        if self.n < 1:
            raise StopIteration
        return self.n


x = MyIter(100)
print(list(x))
print(list(x))

你会得到:

[50.0, 25.0, 12.5, 6.25, 3.125, 1.5625]
[]
  1. 您可以通过更改该类,并在返回__iter__()之前将其重置为原始状态来避免这种情况:

    class MyRepeatableIter:
        def __init__(self, n):
            self.n = n
    
        def __iter__(self):
            self.current_value = self.n
            return self
    
        def __next__(self):
            self.current_value /= 2
            if self.current_value < 1:
                raise StopIteration
            return self.current_value
    

    但这仍然无效:

    y = MyRepeatableIter(100)
    for i, j in zip(y, y):
        print(i, j)
    

    你得到:

    50.0 25.0
    12.5 6.25
    3.125 1.5625
    

    如果尝试在线程中使用此迭代器,或者替换使用两个迭代器的代码,则会遇到相同的问题。

  2. 我知道的最佳解决方案是返回对象(或副本)的新实例,但如果你的课程足够大,你就会遇到内存问题。

    class MyIter:
        def __init__(self, n):
            self.n = n
    
        def __iter__(self):
            return MyIter(self.n)
    
        def __next__(self):
            self.n /= 2
            if self.n < 1:
                raise StopIteration
            return self.n
    
  3. 我很好奇哪个被认为是这个问题的最佳解决方案,没有讨论任何问题(没有副本和交替迭代器)

1 个答案:

答案 0 :(得分:0)

如果您希望您的实例像发电机一样运行,那么只能实施__next__,因此无法使用。在这里,你想要的是MyIter.__iter__返回一个生成器,否则改变实例的任何东西都会影响迭代。

class MyIter:

    def __init__(self, n):
        self.n = n

    def __iter__(self):
        n = self.n
        while n >= 1:
            yield n
            n /= 2

obj = MyIter(10)
list(obj) # [10, 5.0, 2.5, 1.25]
list(obj) # [10, 5.0, 2.5, 1.25]

另请注意,__next__的类实例不会出现问题。实际上,使itertools.product这样的类如此有用的原因。