Python:yieldの使い方(無限の素数を生み出す方式、大量にデータを読み取る考え方)

28489 ワード

yieldの使い方
まずLZはまず、pythonの「generators」とキーワード「yield」を知っている仲間はどのくらいいますか?
1.Pythonジェネレータとは
Pythonジェネレータはyieldを呼び出してジェネレータ反復器(反復可能なオブジェクトのみ)に戻る関数です.yieldは1つの値で呼び出すことができます.この場合、この値は「生成された」値とみなされます.次にジェネレータ反復器でnext()を呼び出すと(たとえばforループの次のステップで)、ジェネレータは関数の先頭から実行を再開するのではなくyieldを呼び出す位置から実行を再開します.ローカル変数の値のように、すべてのステータスが復元され、次の呼び出しyieldまでジェネレータが実行されます.PS:まだよく理解していないようですが、大丈夫です.ゆっくり理解してみましょう
2.一例(ウォーミングアップ)
import numpy as np

list_test = [1, 2, 3, 4]
list_new = [x*x for x in list_test]#              in     
print("list_new: ", list_new)
list_new:  [1, 4, 9, 16]
#  []  (),    my_gen          ,         ,        
my_gen = (x*x for x in list_test)
print("my_gen: ", my_gen)
my_gen:   at 0x7f7eca184750>
#             ?    for          ,  next       
#       ,           ,next!
for i in range(len(list_test)):
    print("my_gen: ", next(my_gen))
my_gen:  1
my_gen:  4
my_gen:  9
my_gen:  16
#            ,             ?
#            yield!!!!  ,          
def g():
    print('1')
    x = yield 'hello'
    print('2', 'x= ', x)
    y = 5 + (yield x)
    print('3', 'y= ', y)

#        ,            
f = g()    
print(f)

#  next()     yield  
next(f)
1
'hello'
#           ,send,        x = 5
f.send(5)
2 x=  5
5
#     x =2,                ,     yield    ,
#             ,           !
f.send(2)
3 y=  7
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

 in 
----> 1 f.send(2)


StopIteration: 

3.無限の素数を生成する方法(yieldを使用)
#             ,         return,        ,
#            ,      ,           ,         ,
#             

#         ,          ,               ,       
#     ,    
def get_primes(input_list):
    result_list = list()
    for element in input_list:
        if is_prime(element):
            result_list.append(element)
    return result_list


#      ,    1    ,  1     ,                
def is_prime(number):
    if number > 1:
        if number == 2:
            return True
        if number % 2 == 0:
            return False
        for current in range(3, int(np.sqrt(number)+1), 2):
            if number % current == 0:
                return False
        return True
    return False
        
#               ,      ,        ?
get_primes([x for x in range(10)])
[2, 3, 5, 7]

私たちは限られた数字のリストに対して素数の判断をしただけですが、10000個以上が必要な場合、あなたはどのように確定することができますか?これは問題です.次に、関数を書いてこの要求を満たすことができても、メモリはこんなに多くのデータを格納するために大きなメモリを消費します.これはアルゴリズム的に実行可能であっても、メモリ消費上無視できない問題です.
#            ,  magical_infinite_range,             ,
#        ,                ?
def get_primes(start):
    for element in magical_infinite_range(start):
        if is_prime(element):
            return element
#         ,         :https://projecteuler.net/problem=10
def solve_number_10():
    # She *is* working on Project Euler #10, I knew it!
    total = 2
    for next_prime in get_primes(3):
        if next_prime < 2000000:
            total += next_prime
        else:
            print(total)
            return

上記の関数を詳しく分析すると、対応する問題を解決できないようで、solve_まで実行しています.number_10この関数を実行するとget_primes(3)の場合,関数はreturnによって1つの3だけ返されて終了し,無限の素数を得ようとする問題は解決されていないが,いったいどのように解決すればよいのだろうか.もちろんジェネレータを使用しましょう.これにより、無限の数の需要が解決され、バッチ全体のデータが大量のメモリを消費する問題も解決されます.
#           ,    for loop        
def simple_generator_function():
    yield 1
    yield 2
    yield 3
    
for value in simple_generator_function():
    print(value)
1
2
3
our_generator = simple_generator_function()
next(our_generator)
1
our_generator.__next__()
2
next(our_generator)
3
our_generator = simple_generator_function()
for value in our_generator:
    print(value)
1
2
3
#        ,    ,      ,        ,              ?
#         
print(next(our_generator))
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

 in 
----> 1 print(next(our_generator))


StopIteration: 
# however, we can always create a new generator
# by calling the generator function again...
#                 ,            ,         
new_generator = simple_generator_function()
print(next(new_generator)) # perfectly valid
1
def get_primes(number):
    while True:
        if is_prime(number):
            yield number
        number += 1
def solve_number_10():
    # She *is* working on Project Euler #10, I knew it!
    total = 2
    for next_prime in get_primes(3):
        if next_prime < 2000000:
            total += next_prime
        else:
            print(total)
            return
#                ,       ,           ?
solve_number_10()
142913828922

ジェネレータの動作原理をよりよく理解するには、まずsolveを実行する必要があります.number_10()では、対応する関数に入り、4行目の位置に行き、get_に入ります.primes(3)という関数はwhile(True)が常に真後にループに入り,3判断結果は素数なので3を返しsolver_に戻るnumber_10()関数のforループ内でtotalの数値は2+3=5で、get_を再実行します.primes(3).
小さな黒板が立って、ポイントをつけます!!再びget_へprimes(3)の場合、最初から運転を開始するのではなく、number+=1という行を実行する、すなわち、我々が得たnumber=4は、whileサイクルで判断され、number=5の場合、素数が1つ得られ、戻ると、我々が得た素数が200000を超えるまで繰り返し、終了する.
では、どうやって値をジェネレータに返しますか?
今、私たちはまた1つの問題に直面しました:今回、私たちは最小素数が10より大きくて、それから100で、それから1000であることを望んで、このようにして、つまり私たちは10100000より大きい...最小素数を得る必要があります.
def print_successive_primes(iterations, base=10):
    # like normal functions, a gnerator function
    # can be assigned to a variable
    prime_generator = get_primes(base)
    prime_generator.send(None)  
    for power in range(iterations):
        print(prime_generator.send(base**(power+1)))

            
def get_primes(number):
    while True:
        if is_prime(number):
            number = yield number
        number += 1
print_successive_primes(10)
11
101
1009
10007
100003
1000003
10000019
100000007
1000000007
10000000019

まずgeneratorを印刷しますsendの結果、sendはジェネレータに値を送信し、ジェネレータが生成した値(yieldがgenerator関数の内部でどのように動作するかを反映する)を返すため、これは可能である.
次にprime_に注意generator.send(None)の行は、sendを使用してジェネレータを「起動」する場合(すなわち、ジェネレータ関数の最初の行から最初のyield文までのコードを実行する場合)、Noneを送信する必要があります.これは、定義に従ってジェネレータが最初のyield文に到達していないため、実際の値を送信すると、それを「受信」できるものはありません.ジェネレータが起動すると、上記のように値を送信できます.
4.最後にもう一つの消費者と生産者のモデルを挙げる
import random 
def get_data():
    """return 3 random integers between 0 and 9"""
    return random.sample(range(10), 3)

def consume():
    """Display a running average across lists of integerss sent to it"""
    running_sum = 0
    data_items_seen = 0 
    while True:
        data=yield
        data_items_seen += len(data)
        running_sum += sum(data)
        print('The running average is {}'.format(running_sum/float(data_items_seen)))

def peroduce(sonsumer):
    """Produce a set of values and forwards them to 
    the pre-trained consumer function"""
    while True:
        data = get_data()
        print('Produced {}'.format(data))
        consumer.send(data)
        yield
        
if __name__=='__main__':
    consumer = consume()
    consumer.send(None)
    producer = peroduce(consumer)
    
    for _ in range(10):
        print('Producing ...')
        next(producer)
    

Producing ...
Produced [8, 6, 9]
The running average is 7.666666666666667
Producing ...
Produced [6, 4, 5]
The running average is 6.333333333333333
Producing ...
Produced [5, 7, 6]
The running average is 6.222222222222222
Producing ...
Produced [7, 1, 8]
The running average is 6.0
Producing ...
Produced [4, 9, 1]
The running average is 5.733333333333333
Producing ...
Produced [7, 6, 0]
The running average is 5.5
Producing ...
Produced [4, 7, 9]
The running average is 5.666666666666667
Producing ...
Produced [5, 4, 0]
The running average is 5.333333333333333
Producing ...
Produced [2, 6, 0]
The running average is 5.037037037037037
Producing ...
Produced [0, 2, 6]
The running average is 4.8

5.まとめ:
以下にいくつかのまとめがあります.非常に重要で、仲間たちに役立つことを望んでいます.
1.ジェネレータの目的は一連の数値(フィボナッチ数列など)を生成することである.yieldは実は関数のreturnに似ていますが、ジェネレータ関数の変数は対応して保存されます.ジェネレータは特殊な反復器である.反復器のようにnext()を使用してジェネレータから次の値を取得したり、sendを使用して対応する値5を強制的に転送したりすることができます.for loopによって暗黙的にnext()を呼び出して次の値6を取得することもできる.生成の利点はメモリを節約できることであり、一度にすべての値を返す必要はなく、必要に応じて対応するデータを取得することである.これはLZに実際に深い学習を連想させ、私たちはデータを読み取る時、常に大量のデータを読み取る必要があり、データ量が大きすぎる場合、一度にすべてのメモリを読み込むことはできない.最後の方法は、このようなジェネレータなどの類似の方法を使用することです.
6.参照先:
1.https://www.jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/#fn:prime 2.https://www.jianshu.com/p/d09778f4e055 3.https://www.jianshu.com/p/84df78d3225a