原文:『実用的なPythonプログラミング』06_02_Customizing_iteration

7119 ワード

目次|前節(6.1反復プロトコル)|[次節(6.3生産者/消費者)]()
6.2カスタム反復
このセクションでは、ジェネレータ関数を使用して反復をカスタマイズする方法について説明します.
に質問
反復モードをカスタマイズしたいとします.
例:逆数:
>>> for x in countdown(10):
...   print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>

これを行うにはjの簡単な方法がある.
ビルダー
ジェネレータ(generator)は反復を定義する関数です.
def countdown(n):
    while n > 0:
        yield n
        n -= 1

例:
>>> for x in countdown(10):
...   print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
yield文を使用した関数をジェネレータと呼びます.
ジェネレータ関数の動作は、通常の関数とは異なります.ジェネレータ関数を呼び出すと、すぐに関数を実行するのではなく、ジェネレータオブジェクト(generator object)が作成されます.
def countdown(n):
    # Added a print statement
    print('Counting down from', n)
    while n > 0:
        yield n
        n -= 1
>>> x = countdown(10)
# There is NO PRINT STATEMENT
>>> x
# x is a generator object

>>>

ジェネレータ関数は、__next__()メソッドが呼び出されたときにのみ実行されます.
>>> x = countdown(10)
>>> x

>>> x.__next__()
Counting down from 10
10
>>>
yieldは値を生成するが、suspend関数は実行される.ジェネレータ関数は、__next__()メソッドが次回呼び出されると復元されます.
>>> x.__next__()
9
>>> x.__next__()
8

ジェネレータが最後の値を返すと、再び反復するとエラーがトリガーされます(注:StopIteration).
>>> x.__next__()
1
>>> x.__next__()
Traceback (most recent call last):
File "", line 1, in ? StopIteration
>>>

観測:ジェネレータ関数で実装されるプロトコルは、for文がリスト、メタグループ、辞書、ファイルで使用される下位プロトコルと同じです.
練習する
練習6.4:簡単なジェネレータ
反復をカスタマイズする場合は、ジェネレータ関数を常に考慮する必要があります.ジェネレータ関数は簡単に記述できます.必要な反復論理を実行する関数を作成し、yieldを使用して値を送信します.
たとえば、ファイルの行ごとに一致するサブストリングを検索するジェネレータを作成します.
>>> def filematch(filename, substr):
        with open(filename, 'r') as f:
            for line in f:
                if substr in line:
                    yield line

>>> for line in open('Data/portfolio.csv'):
        print(line, end='')

name,shares,price
"AA",100,32.20
"IBM",50,91.10
"CAT",150,83.44
"MSFT",200,51.23
"GE",95,40.37
"MSFT",50,65.10
"IBM",100,70.44
>>> for line in filematch('Data/portfolio.csv', 'IBM'):
        print(line, end='')

"IBM",50,91.10
"IBM",100,70.44
>>>

これは興味深いアイデアです.関数にカスタム処理プロセスを非表示にし、forループに適用することができます.次の例はもっと尋常ではない状況を探究する.
練習6.5:ストリーム・データ・ソースの監視
ジェネレータは、ログ・ファイル、株式市場メッセージなどのリアルタイム・データ・ソースの監視に適用できます.このセクションでは、ジェネレータを使用してリアルタイムのデータソースを監視するという考え方を探ります.まず、以下の説明に厳格に従ってください.Data/stocksim.pyは、株式市場のデータを模倣し、リアルタイムのデータをData/stocklog.csvファイルに絶えず書き込むために使用される.独立したコマンドラインウィンドウを開き、Data/ディレクトリに入り、stocksim.pyプログラムを実行してください.
bash % python3 stocksim.py

Windowsシステムを使用している場合は、stocksim.pyファイルを見つけて、ファイルをダブルクリックして実行します.次に、まずこのプログラムを片側に置いて(ずっとそこで実行させる)、別のコマンドラインウィンドウを開き、シミュレーションプログラム(訳注:stocksim.py)によってデータが書き込まれているData/stocklog.csvファイルを表示します(訳注:Linuxシステムを使用している場合は、Dataディレクトリの下に入り、tail -f stocklog.csvコマンドで表示できます).Data/stocklog.csvファイルに数秒おきに新しいテキスト行が追加されていることがわかります.同様に、バックグラウンドでプログラムを実行させます.このプログラムは数時間実行されます(心配しないでください).stocksim.pyプログラムが実行されたら、Data/stocklog.csvファイルを開き、ファイルの最後に移動し、新しい出力を表示するプログラムを作成します.ワークディレクトリの下にfollow.pyファイルを作成し、次のコードを入れてください.
# follow.py
import os
import time

f = open('Data/stocklog.csv')
f.seek(0, os.SEEK_END)   # Move file pointer 0 bytes from end of file

while True:
    line = f.readline()
    if line == '':
        time.sleep(0.1)   # Sleep briefly and retry
        continue
    fields = line.split(',')
    name = fields[0].strip('"')
    price = float(fields[1])
    change = float(fields[4])
    if change < 0:
        print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
follow.py プログラムを実行すると、リアルタイムの株価オファー(stock ticker)が表示されます.follow.py のコードは、Unixシステムがログファイルを表示するtail -fコマンドに似ています.
注意:この例では、readline()メソッドの使用は、通常ファイルからローを読み出す方法とは少し異なります(通常はforループを使用します).この場合、readline()を使用して、プローブファイルの最後を繰り返し、新しいデータが追加されたかどうかを確認します(readline()メソッドは、新しいデータまたは空の文字列を返します).
練習6.6:ジェネレータを使用してデータを生成する
練習6.5のコードを見てみると、コードの最初の部分は数行のデータを生成し、whileサイクルの最後の文はデータを消費していることがわかります.ジェネレータの主な特性は、生成されたデータのコードを再利用可能な関数に移動できることです.
ジェネレータ関数follow(filename)を介してファイル読み取りを実行するために、練習6.5のコードを変更してください.次のコードが動作するように変更してください.
>>> for line in follow('Data/stocklog.csv'):
          print(line, end='')

... Should see lines of output produced here ...

株価見積コードを修正して、コードが次のように見えるようにしてください.
if __name__ == '__main__':
    for line in follow('Data/stocklog.csv'):
        fields = line.split(',')
        name = fields[0].strip('"')
        price = float(fields[1])
        change = float(fields[4])
        if change < 0:
            print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')

練習6.7:株式ポートフォリオの表示follow.pyプログラムを変更して、プログラムが株式データストリームを表示し、株式ポートフォリオの株式の情報を印刷できるようにしてください.例:
if __name__ == '__main__':
    import report

    portfolio = report.read_portfolio('Data/portfolio.csv')

    for line in follow('Data/stocklog.csv'):
        fields = line.split(',')
        name = fields[0].strip('"')
        price = float(fields[1])
        change = float(fields[4])
        if name in portfolio:
            print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')

注意:このコードを実行するには、Portfolioクラスがin演算子をサポートする必要があります.練習6.3を参照して、Portfolioクラスが__contains__()演算子を実装していることを確認してください.
ディスカッション
ここでは、ファイルの最後にローを読み込む興味深い反復モードを関数に移動します.follow()関数は、現在、任意のプログラムで使用できる完全に汎用的なユーティリティです.たとえば、follow()関数を使用して、サーバ・ログ、デバッグ・ログ、その他の類似のデータ・ソースを表示できます.
目次|前節(6.1反復プロトコル)|[次節(6.3生産者/消費者)]()
注:完全な翻訳はhttps://github.com/codists/practical-python-zhを参照