基于生成器编写python迭代器的最佳方法是什么?

时间:2013-01-06 22:01:36

标签: python iterator generator

我一直在寻找一种编写基于生成器的Python迭代器的好方法。我发现了很多关于迭代器主题的教程,很多关于生成器和yield语句的教程,但没有结合这两者的内容。我已经构建了一个有效的小例子,并想知道是否有更好的方法来做到这一点。

class myIterator :

    def __init__(self, n) :
        self.last = n
        self.myGen = self.myGenerator() 

    def __iter__(self) :
        return self.myGenerator()

    def next(self) :
        return self.myGen.next()

    def myGenerator(self) :
        prev = 0
        fib = 1
        while fib < self.last :
            res = fib
            yield res
            fib = fib + prev
            prev = res

        raise StopIteration

我在真实世界的程序中使用了这种技术,可以在SQLStatements.py in my Github repository

找到

最令人困惑的部分是定义next()函数。显而易见的解决方案在每次调用时都返回了第一个元素。存储包含生成器的实例变量有效,但似乎是一个kludge。

如果你知道一个更好的方法来做这个或一个涵盖这个主题的好教程,请告诉我。

编辑: @MartijnPieters发布的第三个例子完全解决了这个问题。保存self.next中的self.generator.next函数提供了下一个函数。我希望这有助于其他人试图解决这个问题。

2 个答案:

答案 0 :(得分:8)

迭代器协议由两部分组成。 __iter__方法是最重要的方法,当您在对象上使用iter()时,它应该返回迭代器。

只需将__iter__的正文替换为myGenerator;无需提出StopIteration

class myIterator:
    def __init__(self, n):
        self.last = n

    def __iter__(self):
        prev = 0
        fib = 1
        while fib < self.last:
            res = fib
            yield res
            fib += prev
            prev = res

现在iter(myIterator(10))是一个迭代器,并且有一个.next()方法。

如果您希望您的类直接用作迭代器,则需要__iter__返回self并提供.next()方法。

.next()方法为每个函数体生成一个元素(并使用return somevalue)。生成器只是一个使用yield的函数,实际上,这样的方法本身为你实现了迭代器协议(包括.next())。

如果你使用.next(),它将会是这样的:

class myIterator:
    def __init__(self, n):
        self.last = n
        self.prev = 0
        self.fib = 1

    def __iter__(self):
        return self

    def next(self):
        if self.fib < self.last:
            res = self.fib
            self.fib += self.prev
            self.prev = res
            return res

        raise StopIteration

您可以重复使用生成器的.next()方法:

class myIterator:
    def __init__(self, n):
        self.last = n
        self.next = self.myGenerator().next  # Use the generator `.next`

    def __iter__(self):
        return self

    def myGenerator(self):
        prev = 0
        fib = 1
        while fib < self.last:
            res = fib
            yield res
            fib += prev
            prev = res

答案 1 :(得分:3)

生成器 已经是迭代器。没有必要包装它:

>>> def gen():
...  yield 1
...  yield 2
...  yield 3
... 
>>> 
>>> a = gen()
>>> dir(a)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']
>>> ai = a.__iter__()
>>> ai
<generator object gen at 0x542f2d4>
>>> a
<generator object gen at 0x542f2d4>
>>> a.next()
1
>>> a.next()
2
>>> a.next()
3
>>> a.next()
Traceback (most recent call last):
  File "<string>", line 1, in <module>
StopIteration