pythonジェネレータとコモンシップ

4056 ワード

yield
pythonジェネレータのyieldの場合、yield itemはnext()の呼び出し元に値を生成し、next()を呼び出すまでジェネレータを停止します.
def func():
    for i in range(10):
        yield i

f = func()
next(f) 

きょうてい
コパスでは、yieldは通常、式の右側(data=yield)に表示され、値を返すことも、返さないこともできます(yieldの後ろに式がない場合はNoneを返します).ジェネレータの呼び出し元はsend()メソッドを使用してデータを送信でき、送信されたデータはyield式の値になります.したがって、ジェネレータは、コヒーレントとして使用することができる.
コラボレーションは、呼び出し元と協力して呼び出し元から提供される値を生成するプロセスです.
>>> def func():
...     for i in range(10):
...             r = yield i    #  yield        ,   i
...             print(r)
... 
>>> f = func()
>>> next(f)     #                
0
>>> f.send(10)
10
1

***f.send()を使用する前に、必ずプレアクティブ***
コンポジットの使い方を簡略化するために、プレエキサイティングアクセサリーを使用することができます.
import functools
def wrapper(func):
    @functools.wraps(func)    #   wraps     func    
    def inner(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return inner


def func():
    for i in range(10):
        r = yield i
        print(r)

コンシステントおよび例外処理の終了
コンシステント内に例外が処理されていない場合、コンシステントは終了し、コンシステントが再び呼び出され、StopIteration例外が放出されます.
>>> f = func()
>>> f.send(10)
100
1
>>> f.send('hello')
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 5, in func
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
>>> f.send(10)
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

コンシステントを値に戻す
コンシステントでreturnを使用して返される値は、StopIteration例外のvalue変数に配置され、この例外をキャプチャして取得できます.
yield from
yield fromを使用すると、StopIteration例外が自動的にキャプチャされ、value変数の値がyield from式の値になります.
def func():
    for i in range(10):
        r = yield i
    
yield from rang(10)    func()

yield fromは呼び出し方法を内層のサブジェネレータに接続する、外層の呼び出し方法は内層のジェネレータに直接値を伝達することができ、サブジェネレータが返す値はyield fromによって受信する.例を見てみましょう
#!/usr/bin/env python3
from collections import namedtuple

Result = namedtuple('Result', 'count average')#     

#             averager     ,              

def averager():
    total = 0.0
    count = 0
    average = None
    while True:        # main           
        term = yield
        if term is None: #     
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average) #    Result    grouper   yield from     


#      
def grouper(results, key):
    #             averager   ,                  
    while True:        # grouper           yield from   ,      averager   。grouper  yield from      ,  averager           。averager       ,       results[key]  。while        averager  ,      。
        results[key] = yield from averager()
    
#    
def main(data):
    results = {}
    for key, values in data.items():
        # group    grouper          ,  grouper          results,      ;       
        group = grouper(results, key)
        next(group)        
        for value in values:            #    value  grouper         averager   ;
            # grouper          ,  grouper   yield from   
            group.send(value)        #  None  groupper,        averager   ,        。           。
        #     group.send(None),  averager          ,              ,     result[key]  
        group.send(None)
    report(results)
        
#     
def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(';')
        print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))

data = {
    'girls;kg':[40, 41, 42, 43, 44, 54],
    'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],    
    'boys;kg':[50, 51, 62, 53, 54, 54],    
    'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
}

if __name__ == '__main__':
    main(data)