Pythonのデコレータの基本と応用例

Pythonでは、デコレータは関数やクラスをラップすることで、その機能を拡張するための設計パターンです。元の関数やクラスのコードを変更することなく、新たな機能を追加できます。以下は、Pythonのデコレータパターンを用いた基本的な例です。

import time

# 単純なログ出力デコレータを定義
def logging_decorator(func):
    def inner_function(*args, **kwargs):
        print(f"関数 '{func.__name__}' を呼び出します。")
        result = func(*args, **kwargs)
        print(f"関数 '{func.__name__}' が '{result}' を返しました。")
        return result
    return inner_function

# 元の関数
def calculate_sum(x, y):
    return x + y

# デコレータを使って関数をラップ
calculate_sum = logging_decorator(calculate_sum)

# デコレートされた関数を呼び出す
print(calculate_sum(10, 20))

この例では:

  • logging_decoratorはデコレータ関数で、引数として関数(ここではcalculate_sum関数)を受け取ります。
  • デコレータの内部で、inner_functionという新しい関数が定義されています。この関数には、元の関数の前後に実行したい追加のロジック(ログ情報の表示など)が含まれています。
  • inner_functionは、渡された元の関数func(*args, **kwargs)を呼び出し、その戻り値を処理します。
  • 最終的に、デコレータはinner_functionオブジェクトを返します。
  • calculate_sum = logging_decorator(calculate_sum)の行で、実際に関数がデコレートされています。つまり、元の関数への参照が、デコレートされた新しい関数に置き換えられます。

また、デコレータは@記号を使ってより簡潔に適用できます。

# @記号を使ってデコレータを適用
@logging_decorator
def calculate_sum(x, y):
    return x + y

# デコレータを手動で呼び出す必要はなく、デコレートされた関数を直接呼び出せます
print(calculate_sum(10, 20))

次に、デコレータを使って新しいスレッドで関数を実行し、非同期で動作させるものを作成します。以下はその簡単な例です。

import threading

# 非同期実行のためのデコレータ
def run_in_thread_decorator(func):
    def executor(*args, **kwargs):
        # 新しいスレッドを作成し、実行する関数とその引数を渡す
        thread = threading.Thread(target=func, args=args, kwargs=kwargs)
        # スレッドを開始
        thread.start()
    return executor

# 元の関数
def process_data(task_name, duration):
    print(f"タスク '{task_name}' を開始します。")
    time.sleep(duration)  # 時間のかかる処理をシミュレート
    print(f"タスク '{task_name}' が完了しました。")

# デコレータを使って関数をラップ
process_data_async = run_in_thread_decorator(process_data)

# デコレートされた関数を呼び出す
process_data_async("データ処理1", 3)
process_data_async("データ処理2", 2)

print("メインスレッドは引き続き実行を続けます...")

この例では:

  • run_in_thread_decoratorはデコレータ関数で、引数として関数(ここではprocess_data関数)を受け取ります。
  • デコレータの内部で定義されたexecutor関数は、新しいスレッドを作成し、元の関数とその引数をスレッドのtargetパラメータに渡してから、スレッドを開始します。
  • process_data関数をデコレータでラップした後、その関数を呼び出すと、実際には元の関数が新しいスレッドで実行されます。メインプログラムはすぐに戻り、後続のコードの実行を続けます。

タグ: Python デコレータ 関数 スレッド

5月31日 11:52 投稿