my.code(); Logomy.code();
Python-8.ジェネレータとデコレータ

my.code(); Logomy.code();

  • C++
    • 0.C++の世界へようこそ
    • 1.型システムと制御構造
    • 2.データ集合とモダンな操作
    • 3.ポインタとメモリ管理
    • 4.関数と参照渡し
    • 5.プロジェクトの分割とビルド
    • 6.クラスの基礎
    • 7.クラスを使いこなす
    • 8.継承とポリモーフィズム
    • 9.テンプレート
    • 10.STL ①:コンテナ
    • 11.STL ②:アルゴリズムとラムダ式
    • 12.RAIIとスマートポインタ
  • JavaScript
    • 0.JavaScriptへようこそ
    • 1.基本構文とデータ型
    • 2.制御構文
    • 3.関数とクロージャ
    • 4.'this'の正体
    • 5.オブジェクトとプロトタイプ
    • 6.クラス構文
    • 7.配列とイテレーション
    • 8.非同期処理①: Promise
    • 9.非同期処理②: Async/Await
  • Python
    • 0.環境構築と基本思想
    • 1.基本構文とデータ型
    • 2.リスト、タプル、辞書、セット
    • 3.制御構文と関数
    • 4.モジュールとパッケージ
    • 5.オブジェクト指向プログラミング
    • 6.ファイルの入出力とコンテキストマネージャ
    • 7.例外処理
    • 8.ジェネレータとデコレータ
  • Ruby
    • 0.rubyの世界へようこそ
    • 1.基本構文とデータ型
    • 2.制御構造とメソッド定義
    • 3.すべてがオブジェクト
    • 4.コレクション (Array, Hash, Range)
    • 5.ブロックとイテレータ
    • 6.クラスとオブジェクト
    • 7.モジュールとMix-in
    • 8.Proc, Lambda, クロージャ
    • 9.標準ライブラリの活用
    • 10.テスト文化入門
    • 11.メタプログラミング入門
  • Rust
    • 0.Rustの世界へようこそ
    • 1.基本構文と「不変性」
    • 2.関数と制御フロー
    • 3.所有権
    • 4.借用とスライス
    • 5.構造体とメソッド構文
    • 6.列挙型とパターンマッチ
    • 7.モジュールシステムとパッケージ管理
    • 8.コレクションと文字列
    • 9.エラーハンドリング
    • 10.ジェネリクスとトレイト
    • 11.ライフタイム
  • TypeScript
    • 0.TypeScriptへようこそ
    • 1.基本的な型と型推論
    • 2.オブジェクト、インターフェース、型エイリアス
    • 3.関数の型定義
    • 4.型を組み合わせる
    • 5.ジェネリクス
    • 6.クラスとアクセス修飾子
    • 7.非同期処理とユーティリティ型
my.code(); Logomy.code();

環境構築不要、その場で実践。

ut-code / my-code

Copyright © 2026 ut.code();

ut.code(); について
公式ウェブサイト公式 𝕏 アカウント

第8章: さらに先へ:ジェネレータとデコレータ

この章では、より高度でPythonらしいコードを書くための強力な機能、ジェネレータとデコレータを学びます。これらの機能を使いこなすことで、メモリ効率の良いデータ処理を実装したり、既存の関数の振る舞いをエレガントに変更したりできるようになります。

イテレータとイテラブル

Pythonのforループは非常にシンプルで強力ですが、その裏側ではイテレーションプロトコルという仕組みが動いています。これを理解することが、ジェネレータを学ぶ上での第一歩です。

  • イテラブル (Iterable): forループで繰り返し処理が可能なオブジェクトのことです。リスト、タプル、辞書、文字列などがこれにあたります。内部に __iter__() メソッドを持つオブジェクトと定義されます。
  • イテレータ (Iterator): 「次の値」を返す __next__() メソッドを持ち、値を一つずつ取り出すためのオブジェクトです。イテレータは一度最後まで進むと、それ以上値を取り出すことはできません。

forループは、まずイテラブルオブジェクトの __iter__() を呼び出してイテレータを取得し、次にそのイテレータの __next__() を繰り返し呼び出して要素を一つずつ取り出しています。

REPLで動きを見てみましょう。iter()関数でイテレータを取得し、next()関数で要素を取り出します。

Python 実行環境
ブラウザ上で動作するPython3.13.2のREPL実行環境です。
プロンプト (>>>) の後にコマンドを入力し、Enterキーで実行します。
Ctrl+Cまたは左上の停止ボタンで実行中のコマンドを中断できます。
>>> my_list = [1, 2, 3]
>>> my_iterator = iter(my_list)
>>> type(my_iterator)
<class 'list_iterator'>

>>> next(my_iterator)
1
>>> next(my_iterator)
2
>>> next(my_iterator)
3
>>> next(my_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

最後のnext()呼び出しで StopIteration という例外が発生しているのがわかります。forループはこの例外を検知して、ループを自動的に終了してくれます。

ジェネレータ関数とyieldキーワード

イテレータを自作するには、クラスに __iter__() と __next__() を実装する必要がありますが、少し手間がかかります。そこで登場するのがジェネレータです。ジェネレータは、イテレータを簡単に作成するための特別な関数です。

ジェネレータ関数は、通常の関数と似ていますが、値を返すのにreturnの代わりにyieldを使います。

  • yieldの働き: yieldは値を返すだけでなく、その時点で関数の実行を一時停止し、関数の状態(ローカル変数など)を保存します。次にnext()が呼ばれると、停止した場所から処理を再開します。

これにより、巨大なデータセットを扱う際に、全てのデータを一度にメモリに読み込む必要がなくなります。必要な時に必要な分だけデータを生成するため、非常にメモリ効率が良いコードが書けます。

フィボナッチ数列を生成するジェネレータの例を見てみましょう。

Python 実行環境
ブラウザ上で動作するPython3.13.2のREPL実行環境です。
プロンプト (>>>) の後にコマンドを入力し、Enterキーで実行します。
Ctrl+Cまたは左上の停止ボタンで実行中のコマンドを中断できます。
>>> def fib_generator(n):
...     a, b = 0, 1
...     count = 0
...     while count < n:
...         yield a
...         a, b = b, a + b
...         count += 1
...
>>> f = fib_generator(5)
>>> type(f)
<class 'generator'>

>>> next(f)
0
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

# ジェネレータはもちろんforループで使うことができます
>>> for num in fib_generator(8):
...     print(num, end=' ')
...
0 1 1 2 3 5 8 13

ジェネレータ式

リスト内包表記に似た構文で、より簡潔にジェネレータを作成する方法がジェネレータ式です。リスト内包表記の [] を () に変えるだけで作れます。

リスト内包表記はリストオブジェクトを生成するため、要素数が多いとメモリを大量に消費します。一方、ジェネレータ式はジェネレータオブジェクトを返すため、遅延評価(必要になるまで計算しない)が行われ、メモリ使用量を抑えられます。

Python 実行環境
ブラウザ上で動作するPython3.13.2のREPL実行環境です。
プロンプト (>>>) の後にコマンドを入力し、Enterキーで実行します。
Ctrl+Cまたは左上の停止ボタンで実行中のコマンドを中断できます。
# リスト内包表記
>>> list_comp = [i * 2 for i in range(5)]
>>> list_comp
[0, 2, 4, 6, 8]
>>> type(list_comp)
<class 'list'>

# ジェネレータ式
>>> gen_exp = (i * 2 for i in range(5))
>>> gen_exp
<generator object <genexpr> at 0x...>
>>> type(gen_exp)
<class 'generator'>

>>> next(gen_exp)
0
>>> next(gen_exp)
2
>>> list(gen_exp) # 残りの要素をリストに変換
[4, 6, 8]

巨大なファイルの各行を処理する場合など、ジェネレータ式は非常に有効です。

デコレータの概念と基本的な作り方

デコレータは、既存の関数のコードを一切変更せずに、その関数に新しい機能を追加(装飾)するための仕組みです。これは、関数を受け取って、新しい関数を返す高階関数として実装されます。

ログ出力、実行時間の計測、認証チェックなど、複数の関数に共通して適用したい「横断的な関心事」を扱うのに非常に便利です。

デコレータの基本的な構造は、関数を入れ子にすることです。

  1. 外側の関数(デコレータ関数)は、装飾したい対象の関数を引数として受け取ります。
  2. 内側の関数(ラッパー関数)で、受け取った関数を呼び出す前後に、追加したい処理を記述します。
  3. 外側の関数は、この内側の関数を返します。

関数の実行前後にメッセージを表示する簡単なデコレータを見てみましょう。

Python 実行環境
ブラウザ上で動作するPython3.13.2のREPL実行環境です。
プロンプト (>>>) の後にコマンドを入力し、Enterキーで実行します。
Ctrl+Cまたは左上の停止ボタンで実行中のコマンドを中断できます。
>>> def my_decorator(func):
...     def wrapper():
...         print("--- 処理を開始します ---")
...         func()
...         print("--- 処理が完了しました ---")
...     return wrapper
...

>>> def say_hello():
...     print("こんにちは!")
...

# デコレートされた新しい関数を作成
>>> decorated_hello = my_decorator(say_hello)
>>> decorated_hello()
--- 処理を開始します ---
こんにちは!
--- 処理が完了しました ---

@ 構文

この書き方をより簡単にするための構文が @(アットマーク)、シンタックスシュガーです。

Python 実行環境
ブラウザ上で動作するPython3.13.2のREPL実行環境です。
プロンプト (>>>) の後にコマンドを入力し、Enterキーで実行します。
Ctrl+Cまたは左上の停止ボタンで実行中のコマンドを中断できます。
>>> @my_decorator
... def say_goodbye():
...     print("さようなら!")
...

>>> say_goodbye()
--- 処理を開始します ---
さようなら!
--- 処理が完了しました ---

@my_decorator は、say_goodbye = my_decorator(say_goodbye) と同じ意味になります。こちらのほうが直感的で、Pythonのコードで広く使われています。

ジェネレータとデコレータは、最初は少し複雑に感じるかもしれませんが、使いこなせばよりクリーンで効率的なPythonコードを書くための強力な武器となります。ぜひ積極的に活用してみてください。

この章のまとめ

この章では、Pythonプログラミングをさらに高いレベルへ引き上げるための2つの強力な概念を学びました。

  • ジェネレータ: yieldキーワードを使うことで、メモリ効率に優れたイテレータを簡単に作成できることを学びました。ジェネレータ関数やジェネレータ式を使うことで、巨大なデータストリームや無限シーケンスを、必要な分だけ計算しながら扱うことができます。これは、パフォーマンスが重要なアプリケーションにおいて不可欠なテクニックです。

  • デコレータ: @シンタックスを用いることで、既存の関数のソースコードを変更することなく、機能を追加・変更できることを学びました。デコレータは、ロギング、実行時間計測、アクセス制御といった横断的な関心事を分離し、コードの再利用性を高め、DRY (Don't Repeat Yourself) の原則を維持するのに役立ちます。

これらの機能を使いこなすことは、単に高度な文法を覚えるだけでなく、Pythonの設計思想を理解し、より「Pythonらしい(Pythonic)」コードを書くための重要なステップです。

練習問題1: カウントダウンジェネレータ

countdown(start) というジェネレータ関数を作成してください。この関数は、引数で与えられた start の数値から1まで、1ずつ減っていく数値を順番に yield します。例えば countdown(3) は、3, 2, 1 の順に値を生成します。

ファイルを編集:practice9_1.py
def countdown(start):


# 動作確認
cd_gen = countdown(5)
for i in cd_gen:
    print(i)
python practice9_1.py
ブラウザ上で動作するPython3.13.2の実行環境です。
左上の実行ボタンを押して、このページ内のpractice9_1.pyに書かれている内容を実行します。
(出力例)
5
4
3
2
1

問題2: 実行時間計測デコレータ

関数の実行時間を計測し、"実行時間: X.XXXX秒" のように表示するデコレータ @measure_time を作成してください。このデコレータを、少し時間のかかる処理を行う関数に適用して、動作を確認してみましょう。

ヒント: 時間の計測には time モジュールが使えます。処理の開始前と終了後で time.time() を呼び出し、その差分を計算します。

ファイルを編集:practice9_2.py
import time

def measure_time(func):


# 動作確認用の時間のかかる関数
@measure_time
def slow_function(n):
    print(f"{n}まで数えます...")
    time.sleep(n) # n秒間処理を停止
    print("完了!")

slow_function(2)
python practice9_2.py
ブラウザ上で動作するPython3.13.2の実行環境です。
左上の実行ボタンを押して、このページ内のpractice9_2.pyに書かれている内容を実行します。
(出力例)
2まで数えます...
完了!
実行時間: 2.0021秒
前のページ« 例外処理