3

我一直在寻找一种编写基于生成器的 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

我在一个真实世界的程序中使用了这种技术,该程序可以在我的 Github 存储库中的 SQLStatements.py 中找到

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

如果您知道更好的方法或涵盖此主题的好教程,请告诉我。

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

4

2 回答 2

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

Nowiter(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
于 2013-01-06T22:05:23.720 回答
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
于 2013-01-06T22:16:50.507 回答