工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Industrial programming >> Python

Python ジェネレーター

Python ジェネレーター

このチュートリアルでは、Python ジェネレーターを使用して反復を簡単に作成する方法、反復子や通常の関数との違い、およびそれを使用する理由について学習します。

ビデオ:Python ジェネレーター

Python のジェネレーター

Python で反復子を作成するには、多くの作業が必要です。 __iter__() でクラスを実装する必要があります そして __next__() メソッド、内部状態の追跡、およびレイズ StopIteration 返される値がない場合。

これは長く、直観に反します。このような状況では、ジェネレーターが助けになります。

Python ジェネレーターは、反復子を作成する簡単な方法です。上記のすべての作業は、Python のジェネレーターによって自動的に処理されます。

簡単に言えば、ジェネレーターとは、(一度に 1 つの値を) 反復処理できるオブジェクト (イテレーター) を返す関数です。


Python でジェネレーターを作成する

Python でジェネレーターを作成するのは非常に簡単です。通常の関数を定義するのと同じくらい簡単ですが、yield return の代わりにステートメント

関数に少なくとも 1 つの yield が含まれている場合 ステートメント (他の yield を含む場合があります) または return ステートメント)、ジェネレーター関数になります。両方 yield そして return 関数から何らかの値を返します。

違いは return ステートメントは関数を完全に終了します yield ステートメントは、すべての状態を保存して関数を一時停止し、その後、連続した呼び出しでそこから続行します。


ジェネレーター関数と通常関数の違い

ジェネレーター関数と通常の関数の違いは次のとおりです。

上記のすべてのポイントを説明する例を次に示します。 my_gen() という名前のジェネレータ関数があります いくつかの yield

# A simple generator function
def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n

インタプリタでのインタラクティブな実行を以下に示します。これらを Python シェルで実行して、出力を確認してください。

>>> # It returns an object but does not start execution immediately.
>>> a = my_gen()

>>> # We can iterate through the items using next().
>>> next(a)
This is printed first
1
>>> # Once the function yields, the function is paused and the control is transferred to the caller.

>>> # Local variables and theirs states are remembered between successive calls.
>>> next(a)
This is printed second
2

>>> next(a)
This is printed at last
3

>>> # Finally, when the function terminates, StopIteration is raised automatically on further calls.
>>> next(a)
Traceback (most recent call last):
...
StopIteration
>>> next(a)
Traceback (most recent call last):
...
StopIteration

上記の例で注目すべき興味深い点の 1 つは、変数 n の値が 各呼び出しの間に記憶されます。

通常の関数とは異なり、関数が生成されたときにローカル変数は破棄されません。さらに、ジェネレーター オブジェクトは 1 回だけ反復できます。

プロセスを再開するには、a = my_gen() などを使用して別のジェネレーター オブジェクトを作成する必要があります。 .

最後に、for ループでジェネレータを直接使用できることに注意してください。

これは for ループは反復子を取り、next() を使用して反復します 関数。 StopIteration になると自動的に終了します 上げられます。 Python で for ループが実際にどのように実装されているかについては、こちらをご覧ください。

# A simple generator function
def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n


# Using for loop
for item in my_gen():
    print(item)

プログラムを実行すると、出力は次のようになります:

This is printed first
1
This is printed second
2
This is printed at last
3

ループを含む Python ジェネレーター

上記の例はあまり役に立たず、バックグラウンドで何が起こっているかを理解するためだけに調査しました.

通常、ジェネレーター関数は、適切な終了条件を持つループで実装されます。

文字列を反転するジェネレーターの例を見てみましょう。

def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1, -1, -1):
        yield my_str[i]


# For loop to reverse the string
for char in rev_str("hello"):
    print(char)

出力

o
l
l
e
h

この例では、range() を使用しています。 for ループを使用して逆順でインデックスを取得する関数。

注意 :このジェネレーター関数は、文字列だけでなく、リストやタプルなどの他の種類のイテラブルでも機能します。


Python ジェネレータ式

単純なジェネレーターは、ジェネレーター式を使用してオンザフライで簡単に作成できます。ジェネレーターの構築が容易になります。

匿名関数を作成するラムダ関数と同様に、ジェネレータ式は匿名ジェネレータ関数を作成します。

ジェネレータ式の構文は、Python のリスト内包表記に似ています。ただし、角かっこは丸かっこに置き換えられます。

リスト内包表記とジェネレーター式の主な違いは、リスト内包表記ではリスト全体が生成されるのに対し、ジェネレーター式では一度に 1 つの項目が生成されることです。

彼らは怠惰な実行を持っています(要求されたときだけアイテムを生成します)。このため、ジェネレーター式は、同等のリスト内包表記よりもはるかにメモリ効率が高くなります。

# Initialize the list
my_list = [1, 3, 6, 10]

# square each term using list comprehension
list_ = [x**2 for x in my_list]

# same thing can be done using a generator expression
# generator expressions are surrounded by parenthesis ()
generator = (x**2 for x in my_list)

print(list_)
print(generator)

出力

[1, 9, 36, 100]
<generator object <genexpr> at 0x7f5d4eb4bf50>

上記で、ジェネレーター式が必要な結果をすぐに生成しなかったことがわかります。代わりに、オンデマンドでのみアイテムを生成するジェネレーター オブジェクトを返しました。

ジェネレーターからアイテムを取得する方法は次のとおりです:

# Initialize the list
my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)
print(next(a))

print(next(a))

print(next(a))

print(next(a))

next(a)

上記のプログラムを実行すると、次の出力が得られます:

1
9
36
100
Traceback (most recent call last):
  File "<string>", line 15, in <module>
StopIteration

ジェネレータ式は、関数の引数として使用できます。このように使用する場合、丸括弧は省略できます。

>>> sum(x**2 for x in my_list)
146

>>> max(x**2 for x in my_list)
100

Python ジェネレーターの使用

ジェネレータが強力な実装である理由はいくつかあります。

1.実装が簡単

ジェネレーターは、対応するイテレーター クラスと比較して、明確かつ簡潔な方法で実装できます。以下は、反復子クラスを使用して 2 の累乗のシーケンスを実装する例です。

class PowTwo:
    def __init__(self, max=0):
        self.n = 0
        self.max = max

    def __iter__(self):
        return self

    def __next__(self):
        if self.n > self.max:
            raise StopIteration

        result = 2 ** self.n
        self.n += 1
        return result

上記のプログラムは長くてわかりにくいものでした。では、ジェネレーター関数を使用して同じことを行いましょう。

def PowTwoGen(max=0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

ジェネレーターは自動的に詳細を追跡するため、実装は簡潔でよりクリーンでした。

2.メモリ効率

シーケンスを返す通常の関数は、結果を返す前にメモリ内にシーケンス全体を作成します。シーケンス内の項目数が非常に多い場合、これはやり過ぎです。

このようなシーケンスのジェネレータ実装はメモリに優しく、一度に 1 つのアイテムしか生成しないため、推奨されます。

3.無限の流れを代表する

ジェネレーターは、無限のデータ ストリームを表す優れた媒体です。無限のストリームはメモリに格納できません。ジェネレータは一度に 1 つのアイテムしか生成しないため、データの無限のストリームを表すことができます。

次のジェネレータ関数は、すべての偶数を生成できます (少なくとも理論上)。

def all_even():
    n = 0
    while True:
        yield n
        n += 2

4.ジェネレーターのパイプライン

複数のジェネレーターを使用して、一連の操作をパイプライン処理できます。これは、例を使用して最もよく説明されています。

フィボナッチ数列の数値を生成するジェネレータがあるとします。また、数を 2 乗するためのジェネレーターがもう 1 つあります。

フィボナッチ数列の数値の二乗和を求めたい場合は、ジェネレーター関数の出力を一緒にパイプライン処理することにより、次の方法で実行できます。

def fibonacci_numbers(nums):
    x, y = 0, 1
    for _ in range(nums):
        x, y = y, x+y
        yield x

def square(nums):
    for num in nums:
        yield num**2

print(sum(square(fibonacci_numbers(10))))

出力

4895

このパイプライン処理は効率的で読みやすいです (もちろん、はるかにクールです!)。


Python

  1. Python 演算子
  2. Python 関数の引数
  3. Python 辞書
  4. Python ジェネレーター
  5. Python クロージャー
  6. Python デコレータ
  7. 例を含む Python Lambda 関数
  8. Python abs() 関数:絶対値の例
  9. 例を使用した Python round() 関数
  10. 例を使用した Python map() 関数
  11. Yield in Python チュートリアル:Generator &Yield vs Return の例