Python反復器ジェネレータ

3819 ワード

pythonデータ構造を学習する過程で、反復可能なオブジェクト、反復器、ジェネレータなどの概念が混ざっていて、初心者に霧をかけるのは避けられないので、今日はこれらの概要を撫でてみましょう.
反復可能オブジェクト(iterable)
反復可能オブジェクトとは、一般的にはforループを直接通過できるオブジェクトを反復可能オブジェクトと呼び、isinstance()を使用してオブジェクトがIterableオブジェクトであるかどうかを判断します.
>>>from collections import Iterable >>>isinstance([], Iterable) True >>>isinstance({}, Iterable) True >>>isinstance('123', Iterable) True >>>isinstance(123, Iterable) False
反復可能オブジェクトは特定のデータ型を指すものではありません.list、dict、set、strは反復オブジェクトです.たとえば、開いた状態のfilesなど、socketsも反復可能オブジェクトです.反復可能オブジェクトは、オブジェクトが反復可能であることを表すオブジェクトの属性です.反復可能オブジェクトが実装されました_iter__メソッド.このメソッドは反復オブジェクトを返します.
反復器(iterator)
すべてが実現しました.iter__および_next__メソッドのオブジェクトはすべて反復器(python 2は実装__iter_とnextメソッド),_iter__反復器自体を返します.next__コンテナの次の値を返します.コンテナに要素がない場合は、StopIteration例外が放出されます.isinstance()を使用して、オブジェクトがIteratorオブジェクトであるかどうかを判断できます.
>>>from collections import Iterator >>>isinstance([], Iterator) False >>>isinstance({}, Iterator) False >>>isinstance('123', Iterator) False >>>isinstance((x for x in range(10)), Iterator) True
ここで(x for x in range(10))はジェネレータ式であり、リスト生成式[x for x in range(10)]とは異なるジェネレータオブジェクトを返します.ジェネレータオブジェクトはすべて反復オブジェクトですが、list,dict,strは反復可能オブジェクトですが、反復可能オブジェクトではありません.iter()を使用してlist,dict,strなどの反復可能オブジェクトを反復オブジェクトにすることができます.
>>>isinstance(iter([]), Iterator) True >>>isinstance(iter('123'), Iterator) True
pythonの反復器オブジェクトはデータストリームを表し、このデータストリームを秩序化シーケンスと見なすことができますが、シーケンスの長さは分かりません.next()関数を呼び出して次のデータをオンデマンドで計算するしかありません.そのため、反復器の計算は不活性で、次のデータを返す必要がある場合にのみ計算されます.反復器のこのような特性はメモリのオーバーヘッドを大幅に低減することができ、反復器オブジェクトは無限大のデータストリームを表すことができ、list、dictまたはstrに無限大のデータストリームを格納させることは不可能である.
次に、反復器によってフィボナッチ数列を実現します.
from collections import Iterable
from collections import Iterator

class Fib:
    def __init__(self, max):
        self.n, self.max = 0, max
        self.a, self.b = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.n < self.max:
            self.n += 1
            self.a, self.b = self.b, self.a + self.b
            return self.a
        else:
            raise StopIteration

if __name__ == '__main__':
    fib = Fib(10)
    print(isinstance(fib, Iterable)) # True
    print(isinstance(fib, Iterator)) # True
    print([e for e in fib]) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

  Fibは反復可能なオブジェクトであり(これは__iter_メソッドが実装されているため)、反復器でもある(これは__next_メソッドが実装されているため).インスタンス変数aおよびbは、反復器内部の状態を維持するために使用される.next()メソッドを呼び出すたびに、次のnext()メソッドのステータスを変更し、現在の呼び出しに対して戻り結果を生成する2つのことをします.
反復器は怠け者の工場のように、誰かが必要としたときに値が返され、呼び出されていないときはスリープ状態で次の呼び出しを待っています.
ジェネレータ
ジルコニウムジェネレータは特殊な反復器ですが、この反復器はもっと優雅です.上のクラスのように書く必要はありません.iter__()と_next__()メソッドです.yiledキーワードが1つしか必要ありません.
ジェネレータを用いてフィボナッチ数列を実現する例は、
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        n = n + 1
        a, b = b, b + a
        yield a

f = fib(10)
print(f) # 
print([e for e in f])   # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

fib関数のyieldキーワードは、この関数をジェネレータに変え、f=fib(10)を実行してジェネレータオブジェクトを返すと、関数のコードは実行されず、nextを表示または暗黙的に呼び出すときだけ実際に中のコードが実行され、next()メソッドを呼び出すたびにyield文の戻り値に遭遇して中断します.再実行時に前回返されたyield文から実行を続行します.ジェネレータはpythonの非常に強力な特性であり、他のコンテナオブジェクトよりもメモリを節約し、コードをより少なく使用することで、コードをより優雅にすることができます.以下の構造はジェネレータで再構築できます.
def fun():
    result = []
    for ... in ...:
        result.append(x)
    return result

def fun_gen():
    for ... in ...:
        yield x

まとめ
  • 反復可能オブジェクトが実装されました.iter__メソッド.このメソッドは反復オブジェクトを返します.
  • 反復器は、次の反復戻り値を記録するための内部状態のフィールドを有し、__を実現する.next__および_iter__メソッドでは、反復器はすべての要素を一度にメモリにロードするのではなく、必要に応じて結果を生成します.
  • ジェネレータは、returnではなくyieldを返す特殊な反復器です.