Python初心者でもマスター!yield文の使い方と詳細解説10選

Pythonでのyield文の使い方を説明する記事のサムネイル画像Python

 

【当サイトはコードのコピペ・商用利用OKです】

このサービスはASPや、個別のマーチャント(企業)による協力の下、運営されています。

記事内のコードは基本的に動きますが、稀に動かないことや、読者のミスで動かない時がありますので、お問い合わせいただければ個別に対応いたします。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10000時間以上』を満たすプログラマ集団によって監修されています。

はじめに

Pythonプログラミングに慣れてくると、さまざまな特性を活用してより効率的なコードを書くことが可能になります。

その一つがyield文です。

この記事では、Python初心者でも理解できるように、yield文の基本的な使い方から、高度な活用例まで詳しく解説します。

サンプルコードとともにご紹介するので、ぜひ実際のコードと一緒に学習してみてください。

●Pythonとは

Pythonは、視覚的に読みやすく、シンプルで、とても人気のあるプログラミング言語です。

そのシンプルさと強力さから、データサイエンス、ウェブ開発、自動化など、様々な分野で広く使用されています。

●yield文とは

yield文はPythonの特性の一つで、ジェネレータを作成するために使用されます。

ジェネレータは、反復可能なオブジェクト(リストや文字列など)を一度にすべて生成するのではなく、一度に一つずつ要素を生成することができます。

これにより、大量のデータを扱う際にメモリを節約することが可能となります。

○基本的な使い方

Pythonの関数内でyield文を使うと、その関数はジェネレータになります。

たとえば、次のような関数が考えられます。

このコードでは、0から4までの整数を一つずつ生成しています。

この例では、yield文を用いて一度に一つずつ数値を生成しています。

def generate_numbers():
    for i in range(5):
        yield i

この関数を使用すると、forループを使用して一つずつ数値を取り出すことができます。

下記のコードはその一例です。

for number in generate_numbers():
    print(number)

このコードを実行すると、0から4までの数値が順に表示されます。

しかし、重要な点は、全ての数値が一度に生成されるのではなく、必要になったときだけ生成されるという点です。

○仕組みと特性

yield文は「一時停止」のような機能を持っています。

関数がyieldに到達すると、そこで一時的に停止し、次に呼び出されたときに、停止した場所から再開します。

この特性により、関数は一度に一つだけ要素を返し、次の要素が必要になったときにのみ再開します。

これは大量のデータを扱う際に非常に有用です。

●サンプルコードと解説

さまざまな用途でのyield文の使用例を紹介します。

それぞれのコードについて詳しく解説していきます。

○サンプルコード1:基本的な使い方

上述のように、yield文は関数内で使用され、その関数をジェネレータに変換します。

この基本的な例を再度見てみましょう。

def generate_numbers():
    for i in range(5):
        yield i

このコードを実行すると、generate_numbersはジェネレータオブジェクトを返します。

このジェネレータオブジェクトをイテレーションすると、0から4までの整数が一つずつ返されます。

下記のコードはその一例です。

for number in generate_numbers():
    print(number)

このコードを実行すると、0から4までの数値が順に表示されます。

しかし、一度にすべての数値が生成されるのではなく、必要になった時だけ生成されます。

この特性により、大量のデータを扱う際にメモリを節約できます。

○サンプルコード2:リスト内包表記との組み合わせ

Pythonのリスト内包表記は、簡潔なコードでリストを生成するのに非常に便利です。

しかし、大きなリストを生成するときにはメモリを大量に消費してしまう問題があります。

ここでyield文を利用すると、必要な要素を一つずつ生成して、メモリ消費を抑えることができます。

次のサンプルコードでは、range関数を使って0から9999999までの整数を生成し、その整数が偶数であれば2で割った結果を生成するジェネレータを作成しています。

def half_of_even_numbers(n):
    for i in range(n):
        if i % 2 == 0:
            yield i // 2

gen = half_of_even_numbers(10000000)

この例では、関数half_of_even_numbersではrange関数を使って0からnまでの整数を生成し、その整数が偶数であれば2で割った結果をyield文で返しています。

これにより、メモリを節約しながら必要な要素を一つずつ生成することができます。

実行後のコードは次のようになります。

print(next(gen))  # 0
print(next(gen))  # 1
print(next(gen))  # 2

next関数を使うと、ジェネレータから次の要素を取得することができます。

最初の3回のnext関数の呼び出しでは、それぞれ0、1、2が出力されます。

このように、大量のデータを扱う際には、リスト内包表記よりもyield文を使用したジェネレータがメモリ効率が良いです。

また、処理速度もジェネレータの方が高速です。

次に、リスト内包表記とyield文を組み合わせた例を見てみましょう。

def square_numbers(n):
    return (i ** 2 for i in range(n))

gen = square_numbers(10000000)

このコードでは、0からnまでの整数の二乗を生成するジェネレータを作成しています。

生成されたジェネレータは変数genに格納され、これを用いて次の要素を取得することができます。

実行後のコードは次のようになります。

print(next(gen))  # 0
print(next(gen))  # 1
print(next(gen))  # 4

最初の3回のnext関数の呼び出しでは、それぞれ0(0の二乗)、1(1の二乗)、4(2の二乗)が出力されます。

○サンプルコード3:大きなデータセットでの使用

Pythonのyield文の特性を生かすと、大きなデータセットを扱う際に非常に有用です。

ここでは、大きなデータセットに対する処理の一例として、巨大なリストの各要素に対する操作をジェネレータを使用して行う例を示します。

# 大きなリストを作成
big_list = [i for i in range(1000000)]

def process_data(data_list):
    for data in data_list:
        yield data * 2

for processed_data in process_data(big_list):
    print(processed_data)

このコードでは、大きなリストbig_listを作成し、その各要素を2倍にするprocess_dataというジェネレータを定義しています。

そのジェネレータを使用してリストの各要素を2倍にし、出力しています。

ここで注意すべきは、process_dataがジェネレータであるため、この関数はリストの全要素を一度に処理するのではなく、一つずつ処理を行います。

これにより、大きなデータを効率良く扱うことが可能となります。

このコードを実行すると、大きなリストの各要素が2倍になり、それが順番に出力されます。

それぞれの操作はジェネレータによって一つずつ行われ、全要素を一度にメモリ上に保持する必要がないため、大量のメモリを消費することなく処理を進めることが可能です。

この例は比較的シンプルな例ですが、より複雑な処理やデータ構造に対しても、同様の方法でジェネレータとyield文を利用することが可能です。

これにより、Pythonで大きなデータを扱う際の有用な手段となります。

次に、大きなデータセットに対する処理をさらに最適化するための一例として、ジェネレータを使用したチェーン処理の例を見てみましょう。

def process_data(data_list):
    for data in data_list:
        data = data * 2  # データを2倍
        if data % 3 == 0:  # データが3で割り切れる場合
            yield data

for processed_data in process_data(big_list):
    print(processed_data)

このコードでは、ジェネレータprocess_data内で2倍にした後に、その数値が3で割り切れる場合のみyield文を用いて値を返しています。

これにより、特定の条件を満たすデータのみをフィルタリングし、処理することが可能となります。

このように、ジェネレータとyield文を組み合わせることで、大きなデータに対する複雑な処理を効率的かつメモリ節約に行うことが可能となります。

この性質は、大量のデータを扱うデータ分析や機械学習の分野などで特に有効です。

○サンプルコード4:複数のyield文を持つ関数

Pythonのyield文は複数の場所に配置することが可能です。

それにより、異なる結果を順次生成するジェネレータ関数を作成することができます。

下記のコードでは、1から3までの整数を返し、その後に文字列を返す関数を定義しています。

def 複数のyieldを持つ関数():
    yield 1
    yield 2
    yield 3
    yield "Pythonの世界へようこそ!"

ジェネレータ = 複数のyieldを持つ関数()

for 値 in ジェネレータ:
    print(値)

このコードを実行すると、次の出力が得られます。

1
2
3
Pythonの世界へようこそ!

この例では、複数のyield文を含む関数を作成し、それをジェネレータとして利用しています。

1回目から3回目の呼び出しで1から3までの整数が順に返され、4回目の呼び出しで文字列が返されます。

○サンプルコード5:ジェネレータ式とyield文

Pythonでは、リスト内包表記と同様の形式でジェネレータ式を作成することができます。

これにより、メモリ効率の良いコードを簡潔に書くことができます。

下記のコードでは、ジェネレータ式を用いて0から9までの平方数を順に出力します。

ジェネレータ = (i**2 for i in range(10))

for 値 in ジェネレータ:
    print(値)

このコードを実行すると、次の出力が得られます。

0
1
4
9
16
25
36
49
64
81

この例では、ジェネレータ式を用いて0から9までの数値を2乗して出力しています。

ジェネレータ式は、大きなデータセットを扱う際に特に有用で、yield文と組み合わせることでより強力なツールとなります。

○サンプルコード6:例外処理とyield文

Pythonのジェネレータ関数では、例外処理を用いてエラーを補足することもできます。

下記のコードでは、特定の値をyieldする際に例外を発生させ、それを補足しています。

def 例外処理とyield():
    for i in range(5):
        if i == 2:
            raise ValueError("2は問題のある値です")
        yield i

ジェネレータ = 例外処理とyield()

while True:
    try:
        print(next(ジェネレータ))
    except StopIteration:
        break
    except ValueError as e:
        print(e)

このコードを実行すると、次の出力が得られます。

0
1
2は問題のある値です
3
4

この例では、ジェネレータ関数内で例外を発生させ、その例外を補足してエラーメッセージを出力しています。

これにより、ジェネレータが生成する値に問題がある場合でも適切に対処することができます。

例外処理とyield文の組み合わせは、堅牢なコードを書く上で重要なテクニックです。

●応用例とサンプルコード

さて、Python初心者の方でも簡単に理解していただけるように、yield文の使い方やその詳細な解説を進めてきましたが、ここでは応用例として、具体的なサンプルコードを用いて解説します。

○サンプルコード7:フィボナッチ数列の生成

このコードでは、yield文を使ってフィボナッチ数列を生成する関数を作成しています。

フィボナッチ数列とは、最初の2つの数が0と1で、その後の数が直前の2つの数の和となるような数列です。

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

上記の関数は、0と1から始まり、その後は直前の2つの数の和を返すフィボナッチ数列を生成します。

関数内部で無限ループを行っていますが、yield文により必要な数だけ取り出すことができます。

f = fibonacci()
next(f)
next(f)
next(f)
next(f)
next(f)

上記のコードを実行すると、フィボナッチ数列の最初の5つの数、すなわち「0, 1, 1, 2, 3」が出力されます。

○サンプルコード8:無限リストの生成

このコードでは、yield文を使って特定の範囲の数字を無限に生成する関数を作成します。

def infinite_sequence(start, end):
    while True:
        for i in range(start, end + 1):
            yield i

上記の関数は、指定された範囲の数字を無限に生成します。

ここでも無限ループが使用されていますが、必要な数だけ取り出せるため、無限リストを扱うことが可能です。

inf_seq = infinite_sequence(0, 3)
next(inf_seq)
next(inf_seq)
next(inf_seq)
next(inf_seq)
next(inf_seq)

上記のコードを実行すると、「0, 1, 2, 3, 0」というように、指定された範囲の数字が無限に生成されます。

○サンプルコード9:コルーチンとしての利用

このコードでは、yield文を使用したコルーチンの基本的な使い方を紹介しています。

コルーチンとは、協調的マルチタスクを可能にする手段であり、一時的に関数の実行を停止したり再開したりすることができます。

def simple_coroutine():
    print("コルーチンが開始されました")
    x = yield
    print("コルーチンが再開されました。送られた値:", x)

この例では、simple_coroutineという名前のコルーチンを定義しています。

このコルーチンは、最初に「コルーチンが開始されました」と出力し、その後はyieldで一時的に実行を停止します。

そして、再開されたときには送られた値を出力します。

c = simple_coroutine()
next(c)
c.send(10)

上記のコードを実行すると、「コルーチンが開始されました」が出力され、その後に「コルーチンが再開されました。

送られた値: 10」と出力されます。

これは、next関数でコルーチンを開始し、sendメソッドで値を送って再開しているからです。

○サンプルコード10:ステートマシンの作成

このコードでは、yield文を使用してステートマシンを作成します。

ステートマシンとは、状態とその状態間の遷移を管理するプログラムの一種で、特定の状態に応じて異なる振る舞いをすることが可能です。

def state_machine():
    state = 0
    while True:
        if state == 0:
            print("状態0です。")
            state = yield
        elif state == 1:
            print("状態1です。")
            state = yield
        else:
            print("不明な状態です。")
            state = yield

上記のコードでは、状態を管理する変数stateと、それぞれの状態に応じた処理を持つ無限ループを定義しています。

この無限ループ内でyield文を用いることで、外部からの入力に応じて状態を変更し、それに応じた処理を行うことが可能となります。

s = state_machine()
next(s)
s.send(0)
s.send(1)
s.send(2)

上記のコードを実行すると、「状態0です。」、「状態1です。」、「不明な状態です。」という出力が得られます。

このように、yield文を用いることで、一つの関数内で複数の状態を管理し、それぞれの状態に対応した処理を行うステートマシンを作成することが可能となります。

●注意点と対処法

Pythonのyield文を使用する際には、いくつかの注意点があります。

これらの注意点を理解することで、効率的かつエラーなくコードを書くことができます。

まず一つ目の注意点は、ジェネレータ関数が一度終了すると再度利用することができないという点です。

ジェネレータ関数は一度終了するとその状態は保持されず、再度同じジェネレータ関数を呼び出しても最初からの実行となります。

ですので、ジェネレータ関数を何度も使いたい場合は、その都度ジェネレータ関数を呼び出すようにしましょう。

また、ジェネレータ関数内部でyield文を使う際、ローカル変数の値が保存されることも覚えておいてください。

これはジェネレータ関数が次に呼び出されたときに前回yieldしたときの状態から処理が再開されるためです。

しかし、そのローカル変数を外部から変更することはできません。

これはジェネレータ関数がクロージャーの一種であり、そのスコープが限定されているためです。

さらに、ジェネレータ関数の呼び出しは、一度だけでなく、何度も行うことができます。

ですが、それぞれの呼び出しは互いに独立していて、異なる実行状態を持つことを理解しておきましょう。

したがって、一つのジェネレータ関数を並列に使用する場合には注意が必要です。

以上のような注意点を踏まえ、うまくyield文を利用するための対処法をいくつか紹介します。

1つ目の対処法は、ジェネレータ関数の再利用を考慮することです。

ジェネレータ関数は再利用できないので、同じジェネレータ関数を繰り返し使いたい場合は、都度新たにジェネレータ関数を生成するようにしましょう。

2つ目の対処法は、ジェネレータ関数のローカル変数に依存しない設計を心掛けることです。

ローカル変数はジェネレータ関数内部でのみ利用可能で、外部から変更することはできません。

ですから、ジェネレータ関数が外部の状態に依存しないように、独立したジェネレータ関数を設計すると良いでしょう。

3つ目の対処法は、ジェネレータ関数の呼び出しに注意を払うことです。

一つのジェネレータ関数を並列で使用するときは、それぞれが異なる実行状態を持つことを理解しておく必要があります。

したがって、必要に応じてジェネレータ関数を適切に生成・管理することが重要です。

以上がyield文の使い方と注意点についての解説です。

これらの知識を活用すれば、より高度で効率的なPythonコードを書くことができるでしょう。

まとめ

この記事では、Python初心者でも理解できるように、yield文の基本的な使い方から応用的な使い方までを幅広く紹介しました。

サンプルコードを交えての説明を通じて、yield文の動作原理や特性を理解し、実際のプログラミングに活かすことができるようになったことと思います。

yield文はPythonの強力なツールの一つであり、大規模なデータを扱う際や、独特なコード構造を作り出す際に特に有用です。

しかし、その特性を理解しないまま使用すると予期しない結果を生む可能性もありますので、本記事で紹介した基本的な使い方から応用例、そして注意点と対処法をしっかりと把握しておくことが重要です。

Pythonの世界は深く、様々な機能が提供されています。

今回学んだyield文もその一つですが、まだまだ学べることはたくさんあります。

自身のスキルアップのためにも、これからも新たな学びを探し続けてください。

本記事がPythonプログラミングの一助となれば幸いです。