3

ジェネレーターに基づく Python イテレーターを作成する良い方法を探していました。イテレーターのトピックに関する多くのチュートリアルと、ジェネレーターと yield ステートメントに関する多くのチュートリアルを見つけましたが、2 つを組み合わせたものはありません。動作する小さな例を作成し、これを行うためのより良い方法があるかどうか疑問に思いました。

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 によって投稿された 3 番目の例は、問題を完全に解決します。self.generator.next 関数を self.next に保存すると、次の関数が提供されます。これが、この問題を解決しようとしている他の誰かに役立つことを願っています。

4

2 に答える 2

8

イテレータ プロトコルは 2 つの部分で構成されます。メソッドは最も重要なものであり、オブジェクトで__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()メソッドは、関数本体 (および使用) ごとに1 つの要素を生成します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 に答える