Pythonの高度な特性--ジェネレータ(generator)

9351 ワード

前節の学習では、リスト生成式を使用して、直接リストを作成できることを知っています.ただし,メモリの制約などの実情により,リスト生成式が満たされない場合がある.例えば、長さ1000万のリストでは、普通のメモリが足りないか、実際に処理する過程で、前のいくつかの要素にアクセスするだけで、後ろのほとんどの空間が浪費されています.
考え方:最初は完全なリストを作成するのではなく、ルールを定義することで、ループの過程で後続の要素を絶えず推定し、どの要素を使用してどの要素を生成する効果を達成することができますか?Pythonでは,このメカニズムをジェネレータ:generatorと呼ぶ.
generatorを作成する方法1:
>>> m = (x for x in range(10))
>>> m
 at 0x0376BF00>

観察から,リスト生成式と比較して,最外層の[]を()に置き換えることだけが区別される.mはlistではなくgeneratorであることに注意してください.generatorの各要素をどのように印刷しますか?不器用な方法(この方法はほとんど使えません):
>>> next(m)
0
>>> next(m)
1
>>> l = ['hah','hehe']
>>> next(m)
2

真ん中にエピソードがあり、勝手に操作をして、続いてnext関数を呼び出して、結果はアルゴリズムに従って次の値を計算していることがわかりました.(ジェネレータにより多くの要素がない場合、StopIterationエラーが放出されます)
要素の取り出しの便利な方法:generatorは反復可能なオブジェクトであるため(StopIterationエラータイプから推測することもできます)、forループを使用して取り出しを実現することができます.
>>> n = (a+b for a in 'abc' for b in 'xyz')
>>> for i in n:
...     print(i)
...
ax
ay
az
bx
by
bz
cx
cy
cz

方法2:
上記の中の推定アルゴリズムが複雑であれば,使用方法が実現できない場合には関数を用いて実現することができる.例えば有名なフィポラチ数列(1,1,2,3,5,8,13,21......最初の数と2番目の数を除いて、いずれの数も前の2つの数から加算された和).フィポラチ数列はリスト生成式で書けないので、関数を使って印刷することができます.
>>> def fib(max):
...     n,a,b = 0,0,1
...     while n < max:
...             print (b)
...             a,b = b,a+b#      tuple(b,a+b)   a,b
...             n = n + 1
...     return
...
>>> fib (6)
1
1
2
3
5
8

実は、上記fib()はgeneratorに非常に近いです.print(b)をyield bに変えるだけでいいです.
>>> def fib(max):
...     n,a,b = 0,0,1
...     while n < max:
...             yield b
...             a,b = b,a+b
...             n = n+ 1
...     return
...
>>> fib(6)

これがgeneratorを定義する2つ目の方法です.1つの関数にyieldキーワードが含まれている場合、この関数は通常の関数ではなくgeneratorになります.両者の実行プロセスは、通常の関数が順番に実行され、returnまたは最後の行のコード関数に遭遇すると返されます.一方generatorは、next()が呼び出されるたびに実行され、yield文が返されます.再度実行する場合は、前回返されたyield文から実行を続行します.
forループを使用して反復します.
>>> m = fib(5)
>>> for i in m :
...     print(i)
...
1
1
2
3
5

では、generatorのreturnの値をどのように取得しますか?この場合、StopIterationエラーを取得する必要があります.戻り値はStopIterationのvalueに含まれます.
>>> def fib(max):
...     n ,a,b  = 0,0,1
...     while n < max:
...             yield b
...             a,b = b,a+b
...             n = n+1
...     return 'Over'
...
>>> m = fib(6)
>>> while True:
...     try:
...             x = next(m)
...             print(x)
...     except StopIteration as e:
...             print(e.value)
...             break
...
1
1
2
3
5
8
Over

練習:
楊輝三角:
          1      n=0
         / \      
        1   1     n=1
       / \ / \      
      1   2   1    n=2
     / \ / \ / \      
    1   3   3   1   n=3
   / \ / \ / \ / \    
  1   4   6   4   1  n=4
 / \ / \ / \ / \ / \
1   5   10  10  5   1 n=5
杨辉三角,把二项式系数图形化,把组合数内在的一些代数性质直观的从图形中表现出来,是一种离散型的数与形的优美结合。
有如下规律:
1,每行端点和结尾的数为1;
2、每行数左右对称,由1开始逐渐变大;
3、第n行有n项;
4、第n行数字之和为2的n-1次方;
5、第n行的m个数可表示为C(n-1,m-1),即为从n-1个不同元素中取m-1个元素的组合数;
6、第n行的第m个数和n-m+1个数相等,为组合数性质之一;
7、每个数字等于上一行的左右两个数字之和;(利用此性质可写出整个杨辉三角)
8、(a+b)

n

                    (n+1)      
list, generator, list:
>>> def triangle():
...     l=[1]
...     while True:
...             yield l
...             l.append(0)
...             l= [l[i-1]+l[i] for i in range(len(l))]
...

検証してみます.
>>> x = triangle()
>>> next(x)
[1]
>>> next(x)
[1, 1]
>>> next(x)
[1, 2, 1]
>>> next(x)
[1, 3, 3, 1]
>>> next(x)
[1, 4, 6, 4, 1]
>>> next(x)
[1, 5, 10, 10, 5, 1]
>>> next(x)
[1, 6, 15, 20, 15, 6, 1]
>>> next(x)
[1, 7, 21, 35, 35, 21, 7, 1]
>>> next(x)
[1, 8, 28, 56, 70, 56, 28, 8, 1]
>>> next(x)
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
>>> next(x)
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
>>> next(x)
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
>>> next(x)
[1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]

仕事が終わる!