Python 入門 ノート (48)ジェネレーター

ジェネレーター

ジェネレーターはイテレーターの要素です。

イテレーターは反復処理で、リストのようなものを for loop で回していますが、

ジェネレーターは反復処理をしますがその際、一要素ずつ取り出してそれを生成します。

例を見てみます。

イテレーターの場合には以下の様になります。

l = ['Good morning', 'Good afternoon', 'Good night']
    for i in l:
      print(i)
Good morning
Good afternoon
Good night

 

ジェネレーターの場合も、同様に以下の様に出力されます。

def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good night'

for g in greeting():
    print(g)
Good morning
Good afternoon
Good night

イテレーターと同じように出力されます。

yield は産出するという意味です。ジェネレーターのキーワードになります。

ジェネレーターは yield の後の部分を一つ一つ generate(生成)していきます。

 

では、イテレーターとの違いの部分を見ていきます。

次に以下の様に書くと、同じことが起こります。

g = greeting()
print(next(g))
print(next(g))
print(next(g))

次の様に出力されます。

Good morning
Good afternoon
Good night

そこで、間に print(‘@@@@@@@@@’) を入れてみます。出力されるでしょうか?

g = greeting()
print(next(g))
print('@@@@@@@@@')
print(next(g))
print(next(g))
Good morning
@@@@@@@@@
Good afternoon
Good night

出力されました。

for loop では一気に反復処理がなされますが、

ジェネレーターでは一つ一つ、一旦処理がループを抜けています。

 

最初の next(g) を実行した際 yield 以降を出力後、一旦ループを抜けます
その後に @@@@@@@ を出力します。

その際、一つ目の next(g) を記憶しているので、また次の二つ目の next(g) を出力していきます。

 

run という文字を10回出力する、def counterというジェネレーターを追加します。

ジェネレーターには return はありません。pythonでは yield があるとジェネレーターと判断します。
 
c = counter() でジェネレーターを準備します。
def counter(num = 10):
    for _ in range(num):
      yield 'run'

def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good night'

g = greeting()
c = counter()

print(next(g))  #Good morning

print(next(c))    #run
print(next(c))    #run
print(next(c))    #run
print(next(c))    #run
print(next(c))    #run

print(next(g))    #Good afternoon

print(next(c))    #run
print(next(c))    #run
print(next(c))    #run
print(next(c))    #run
print(next(c))    #run

print(next(g))    #Good night

 

Good morning
run
run
run
run
run
Good afternoon
run
run
run
run
run
Good night

最初の print(next(g)) で Good morning を出力した後に、
print(next(c)) で run を5回出力し、
2回目の print(next(g)) では Good afternoon を出力しています。

つまり、1回目をの出力を記憶していて次の2回目に移っているわけですね。

このように、繰り返し処理の中で、他の処理を挟んだうえで、また処理を続けることが出来ます。

 

また、for i in range(10000000): のような for loop 回数の多い重たい処理が挟まれている場合、一気に処理をせず、小分けにして処理のタイミングをずらしたりできます。

def greeting():
    yield 'Good morning'
    for i in range(10000000):
        print(i)
    yield 'Good afternoon'
    yield 'Good night'

 

最後の print(next(g)) #Good night の後に、更にprint(next(g)) を追加すると、もちろんエラーが返ってきます。
StopIteration という例外処理が返ってきます。

Good night
Traceback (most recent call last):
  File "/home/ec2-user/environment/Myapp.py", line 30, in <module>
    print(next(g))
StopIteration

コメント