【Python】レッスンF-2:デコレーターを理解しよう

ながみえ
【Python学習記事のアイキャッチ画像】LessonF-2 デコレーターを理解しよう

Flaskの学習に入る前にもう少しだけPythonの学習をしましょう。

今回はデコレーターについて見ていきます。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数とスコープ編
Lesson4:データ構造編
Lesson5:オブジェクト指向編
Lesson F:補足学習
・Lesson F-2:可変長引数を理解しよう
・Lesson F-2:デコレーターを理解しよう ◁今回はココ
Python基礎習得者にお勧めの道5選(実務or副業)

Pythonの記事一覧

Python学習カテゴリの親ページ用アイキャッチ画像(テキスト&問題集)、記事一覧へのリンク案内

Flaskの記事一覧

前のページ>>

デコレーターの基本|仕組みと書き方を初心者向けに解説

【Python】勉強猫がノートパソコンを前にして学習を始める様子。記事内の学習スタート用イラスト

デコレーターとは

Pythonでは関数を定義して使うだけでなく、関数に新たな機能を追加するテクニックも存在します。

このような応用的な機能のひとつが「デコレーター」です。

デコレーターは関数の前に「@」を付けて使う、Python独自の強力な文法であり、「ある関数に、別の処理を付け加える」というものです。

Pythonでは関数もオブジェクトとして扱えるため、関数を引数に渡したり、返したりすることができます。

これを活用して、関数の前後に処理を追加するような「ラッパー関数」を使うのが、デコレーターの基本アイデアです。

これは「関数を引数として受け取り、関数を返す」関数の仕組みを応用しており、Lesson3で学んだ関数のスコープや高階関数の知識が土台となっています。

あわせて読みたい
【Python】レッスン3-3:スコープを理解しよう
【Python】レッスン3-3:スコープを理解しよう
あわせて読みたい
【Python】レッスン3-4:高階関数を理解しよう
【Python】レッスン3-4:高階関数を理解しよう

デコレーターの基本構文と使い方

「本体関数(通常の関数)にデコレーターを付与し、処理結果の前後にメッセージを出力するデコレーター関数を考えてみましょう。

デコレーターは以下のように定義し、使用します:

def デコレーター関数名(func):		# デコレーター関数の定義
    def inner_func():				# 内部関数の定義
        print("前処理を実行します")	# 本体関数の前処理の例
        func()						# 本体関数の実行
        print("後処理を実行します")	# 本体関数の後処理の例
    return inner_func

@デコレーター関数名	# デコレーターを付与する宣言
def 本体関数():		# 本体関数(デコレーターを付与される関数)の定義
    print("メインの処理です")

本体関数()	# 本体関数の呼び出し

このコードを実行すると以下のように出力されます。

前処理を実行します
メインの処理です
後処理を実行します

このコードでは、@デコレーター関数名 によって、本体関数をラップするように前後に処理を追加しています。

このように「@デコレーター名」を関数の上に書くことで、自動的にそのデコレーターが適用されます。

デコレーターの使用例

デコレーターは様々な場面で活用できます。

以下は関数の実行時間を測るデコレーターの例です:

import time # タイムモジュールのインポート

def time_logger(func):		# デコレーター関数time_logger()の定義
    def wrapper():			# 内部関数wrapperの定義
        start = time.time()	# 開始時間を計測して変数に代入
        func()				# 本体関数(この場合はsome_process())の実行
        end = time.time()	# 終了時間を計測して変数に代入
        print(f"実行時間: {end - start:.3f}秒")	# 関数の実行時間を出力
    return wrapper

@time_logger
def some_process():	# 関数some_process()の定義
    time.sleep(1)
    print("処理が完了しました")

some_process()		# 関数some_process()の呼び出し

このコードを実行すると、以下のように出力されます。

処理が完了しました  
実行時間: 1.001秒

この例では、time_logger というデコレーターを定義し、some_process の実行時間を測定・表示する処理を追加しています。

このように関数の中身には一切手を加えずに、新しい機能を追加できるのがデコレーターの大きな魅力です。

【Python】勉強猫がノートパソコンを見ながら考え込む様子。記事内の休憩用イラスト

組み込みデコレーターの紹介

Pythonには、初めから用意されている「組み込みデコレーター」もあります。

よく使われるものとして以下があります:

  • @staticmethod:静的メソッドを定義
  • @classmethod:クラスメソッドを定義
  • @property:属性のようにメソッドを呼び出せるようにする
class Sample:
    @staticmethod	# 静的メソッドであることを表すデコレーター
    def greet():
        print("こんにちは!")

Sample.greet()	# インスタンス生成せずにgreetメソッドを呼び出し

このように、デコレーターはクラスや関数の振る舞いを変える便利な仕組みとして広く使われています。

あわせて読みたい
【Python】レッスン5-07:静的メソッドを理解しよう
【Python】レッスン5-07:静的メソッドを理解しよう

まとめ

ここまででデコレーターの仕組み、@記法と基本構文、そして実例と組み込みデコレーターの使い分けを一通り確認しました。

これにより、既存の関数に後から機能を足す、横断的な処理(ログ・計測・検証など)を再利用可能なかたちで共通化するといった実装ができるようになります。

まずは小さな関数から、同じ前処理・後処理を “包んで” 共有する体験を重ねてみてください。

書き換えを最小限に保ちながら、コードは確実に軽く、読みやすくなっていきます。

Q
サイト改善アンケート|ご意見をお聞かせください(1分で終わります)

本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、みなさまの「プログラミングを学習する理由」などをアンケート形式でお伺いしています。

1分だけ、ご協力いただけますと幸いです。

【Python】サイト改善アンケート
1.このサイトをどのように活用していますか?また、今後どのように活用したいですか?
5.気になっているサービス・商品があれば教えてください。
(複数選択可)
※ 特定の記事に関する内容の場合は、記事番号(Python Lesson〇-〇)をご記入願います。 また、ご質問はここではなく問い合わせフォームからお願いします。

練習問題:デコレーターを使ってみよう

この記事で学習した「デコレーター」を復習する練習問題に挑戦しましょう。

【Python】勉強猫がノートパソコンに向かい、練習問題に挑戦する様子。記事内の休憩用イラスト

問題|処理の前後にメッセージを追加しよう

関数の前後にメッセージを表示することで、処理の流れをわかりやすくするプログラムを作成しましょう。

このプログラムでは「デコレーター」を使って、関数の処理前と処理後にメッセージを出力し、さらに処理にかかった時間を表示します。

その上でユーザーから名前を入力してもらい、個別のあいさつを表示します。デコレーターの基本構文と効果を理解しながら実装してみましょう。

以下の要件に従ってコードを完成させてください。

  • time モジュールをインポートすること。
  • デコレーター関数 greeting_logger(func) を定義すること。
  • greeting_logger の中に、処理の前に「=== 処理を開始します ===」、後に「=== 処理が終了しました ===」と出力する処理を記述すること。
  • time.time() を使って、処理の前後で時間を取得し、その差を「実行時間: ◯◯秒」の形式で表示すること(小数点第2位まで)。
  • @greeting_logger を使用して、greet_user() 関数にデコレーターを適用すること。
  • greet_user() 関数の中では、input() を使ってユーザーから名前を入力してもらい、「こんにちは、◯◯さん!ようこそ!」と表示すること。
  • 関数 greet_user() を最後に呼び出すこと。

ただし、以下のような実行結果となるコードを書くこと。

=== 処理を開始します ===
あなたの名前を入力してください:さくら
こんにちは、さくらさん!ようこそ!
=== 処理が終了しました ===
実行時間: 2.01秒

ヒント|難しいと感じる人だけ見よう

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1:実行時間を測るために時刻取得用の標準モジュールを読み込む
 ・時刻を扱う標準モジュールをインポートしておく。

2:関数を包み、開始/終了メッセージと実行時間の計測を差し込むデコレーターを定義
 ・内部でラッパー関数を定義し、外側から受け取った関数をその中で呼び出す。
 ・処理の順序は「開始表示 → 開始時刻記録 → 本体実行 → 終了時刻記録 → 終了表示 → 経過時間表示」。
 ・最後にラッパー関数を返して、@記法で適用できるようにする。

3:デコレーターを適用した挨拶関数を用意し、実行して挙動を確認
 ・関数定義の直前にデコレーター適用の記法を置いてから定義する。
 ・入力の案内と挨拶の文言は実行結果例に合わせると良い。
 ・末尾で関数を1度呼び出し、開始→入力/挨拶→終了→時間の順で表示されるか確認する。

Q
ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

import time

# デコレーター関数:関数の前後でメッセージを表示する
def greeting_logger(func):
    # 内部関数(ラッパー関数)を定義
    '''(穴埋め)ラッパー関数 wrapper を定義する'''
        print("=== 処理を開始します ===")  # 前処理の表示
        start = time.time()  # 開始時間を記録
        '''(穴埋め)本体関数を呼び出す'''
        end = time.time()    # 終了時間を記録
        print("=== 処理が終了しました ===")  # 後処理の表示
        print(f"実行時間: {end - start:.2f}秒")  # 実行時間の出力
    return '''(穴埋め)'''  # ラッパー関数を返す

# デコレーターを使って関数に機能を追加
'''(穴埋め)'''
def greet_user():
    name = input("あなたの名前を入力してください:")  # 入力の取得
    print(f"こんにちは、{name}さん!ようこそ!")  # あいさつメッセージ

# 関数を実行
greet_user()

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

解答例|挨拶ロガープログラム

例えば以下のようなプログラムが考えられます。

Q
正解コード
import time

# デコレーター関数:関数の前後でメッセージを表示する
def greeting_logger(func):
    # 内部関数(ラッパー関数)を定義
    def wrapper():
        print("=== 処理を開始します ===")  # 前処理の表示
        start = time.time()  # 開始時間を記録
        func()               # 本体関数の実行
        end = time.time()    # 終了時間を記録
        print("=== 処理が終了しました ===")  # 後処理の表示
        print(f"実行時間: {end - start:.2f}秒")  # 実行時間の出力
    return wrapper  # ラッパー関数を返す

# デコレーターを使って関数に機能を追加
@greeting_logger
def greet_user():
    """ユーザーの名前を聞いて、あいさつをする関数"""
    name = input("あなたの名前を入力してください:")  # 入力の取得
    print(f"こんにちは、{name}さん!ようこそ!")  # あいさつメッセージ

# 関数を実行
greet_user()

解答例の解説|挨拶ロガープログラムの考え方

解答例の詳細解説は以下の通りです。

Q
詳細解説

import 文:必要なモジュールの読み込み

import time  # timeモジュールを使って処理時間を測定

この行は、関数の処理にかかった時間を測る準備として、時刻を扱うための標準モジュールを読み込んでいます。
後続のデコレーターの中で、処理の前後で現在時刻を取得し、その差を計算して実行時間として表示します。
インポートはスクリプトの冒頭にまとめて書いておくと、どの機能を使うのかがひと目で分かり、コードの見通しも良くなります。

デコレーター関数

# デコレーター関数:関数の前後でメッセージを表示する
def greeting_logger(func):
    # 内部関数(ラッパー関数)を定義
    def wrapper():
        print("=== 処理を開始します ===")  # 前処理の表示
        start = time.time()  # 開始時間を記録
        func()               # 本体関数の実行
        end = time.time()    # 終了時間を記録
        print("=== 処理が終了しました ===")  # 後処理の表示
        print(f"実行時間: {end - start:.2f}秒")  # 実行時間の出力
    return wrapper  # ラッパー関数を返す

ここでは、別の関数を受け取り、その前後に共通の処理を挿入できる仕組みを用意しています。
内側で「ラッパー関数」を定義し、最初に開始メッセージを出してから現在時刻を記録し、本体の関数を実行します。
続けて終了時刻を記録し、終了メッセージを表示したうえで、両時刻の差を計算して実行時間として表示します。
最後に、このラッパー関数を返すことで、@記法を使って元の関数に“後付け”できる形になります。
引数や戻り値を扱わない最小構成の例として、デコレーターの基本的な流れを体験できます。

デコレーターを使って関数に機能を追加

# デコレーターを使って関数に機能を追加
@greeting_logger
def greet_user():
    """ユーザーの名前を聞いて、あいさつをする関数"""
    name = input("あなたの名前を入力してください:")  # 入力の取得
    print(f"こんにちは、{name}さん!ようこそ!")  # あいさつメッセージ

# 関数を実行
greet_user()

ここでは、作成したデコレーターを挨拶用の関数に適用し、実行して一連の流れを体験します。
関数定義の直前に装飾を示す記法を置くことで、呼び出し時にラッパー処理が先に動き、開始メッセージの表示と時間計測が行われます。
続いて、ユーザーに名前の入力を促し、入力値を使った挨拶文を表示します。
最後に終了メッセージと実行時間が並び、前処理→本体→後処理という順序が確認できます。
入力にかかる時間も計測に含まれるため、表示される秒数は実行ごとに変わります。

もっと分かりやすい学習サイトにするために

この記事を読んで「ここが分かりにくかった」「ここが難しかった」等の意見を募集しています。

世界一わかりやすいPython学習サイトにするため、ぜひ 問い合わせフォーム からご意見下さい。

Pythonの記事一覧

Python学習カテゴリの親ページ用アイキャッチ画像(テキスト&問題集)、記事一覧へのリンク案内

Flaskの記事一覧

前のページ>>

デコレーターの疑問解消|FAQと用語のまとめ

初心者がつまずきやすいポイントをFAQとしてまとめ、またよく使う専門用語をわかりやすく整理しました。

理解を深めたいときや、ふと疑問に感じたときに役立ててください。

FAQ|デコレーターに関するよくある質問

Q
Q1. Pythonのデコレーターは何のために使うのですか?

デコレーターは、関数の振る舞いを変更・拡張するために使います。

例えばログ出力やエラーハンドリング、認証処理などを関数の外から追加できるため、コードをより効率的に、かつ読みやすく保つことができます。

Q
Q2. Pythonのデコレーターはクラスに対しても使えますか?

はい、デコレーターは関数だけでなく、クラスにも適用できます。

たとえば、クラス全体の振る舞いをカスタマイズしたい場合に、クラスデコレーターを使用することで、インスタンスの生成処理などに共通ロジックを追加できます。

Q
Q3. 自作のデコレーター関数を作るときに注意すべきポイントは?

自作デコレーターでは、元の関数の情報(関数名やドキュメント文字列など)を維持するために、functools.wrapsを使用することが推奨されます。

これを忘れると、デバッグやドキュメント生成時に不便になることがあります。

Python用語集|デコレーターに関する用語一覧

今回の記事で出てきた用語・関数などを一覧で紹介します。

このサイトに出てくる 全てのPython用語をまとめた用語集 も活用してください。

Python用語定義・使い方の概要解説記事へのリンク
デコレーター関数やメソッドの定義に機能を追加するための構文。@記法 を使って簡潔に記述される本記事
組み込みデコレーターPythonに標準で用意されているデコレーター。例:@staticmethod@property など本記事
ラッパー関数デコレーター内部で元の関数を包んで処理を拡張する関数。内部で *args, **kwargs を使うことが多い本記事
time モジュール時間の取得・測定・待機などを行う標準モジュール。計測や日時の処理に使用される本記事
@classmethodクラスメソッドを定義するためのデコレーター。第1引数にはインスタンスではなくクラス(cls)を取る本記事
@staticmethodインスタンスやクラスに依存しない関数を定義するためのデコレーターLesson5-7
@propertyゲッターを定義し、属性アクセスのようにメソッドを呼び出せるようにするデコレーターLesson5-4
記事URLをコピーしました