Pythonで設計するDecoratorパターン:振る舞いを柔らかく追加する拡張技法 #デザインパターン - Qiita

概要

Decorator(デコレーター)パターンは、
既存のオブジェクトや関数の構造を変更せずに、新しい振る舞いを動的に追加する構造的デザインパターンである。

Pythonでは @decorator 構文によって言語レベルでサポートされており、
関数・メソッド・クラスの振る舞いをシームレスに拡張する非常に重要な機構のひとつである。


1. なぜDecoratorが必要か?

❌ 既存関数のコードを変更して機能を追加

def process():
    # ログ出力を追加したい
    print("Start")
    ...  # 処理本体
    print("End")

→ コードの可読性・保守性が低下し、再利用も困難


✅ デコレーターで機能を“包む”

@with_logging
def process():
    ...

本体に手を加えずに、任意の前後処理を柔軟に追加可能


2. 関数デコレーターの構造

✅ デコレーター関数の基本形

def with_logging(func):
    def wrapper(*args, **kwargs):
        print(f"[LOG] Start {func.__name__}")
        result = func(*args, **kwargs)
        print(f"[LOG] End {func.__name__}")
        return result
    return wrapper

✅ 使用例

@with_logging
def compute():
    print("Computing...")

compute()
[LOG] Start compute
Computing...
[LOG] End compute

3. デコレーターの応用

✅ 引数付きデコレーター(ファクトリ形式)

def with_prefix(prefix):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix}{func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator
@with_prefix(">>>")
def greet():
    print("Hello")

greet()

functools.wraps による関数情報の保持

from functools import wraps

def with_logging(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...
    return wrapper

docstring, 関数名などのメタ情報を保持可能に


4. クラスベースのデコレーター

class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
        print(f"{self.func.__name__} took {end - start:.4f}s")
        return result
@Timer
def work():
    sum(range(10**6))

work()

5. 実務ユースケース

✅ 関数トレーシング・ログ出力

→ 実行タイミング、引数、戻り値の記録


✅ 認証・認可制御(Flaskなどでよく利用)

@app.route("/admin")
@requires_admin
def admin_panel():
    ...

✅ リトライ機構やキャッシュ制御

@retry(times=3)
def unstable_operation():
    ...

✅ テストコードにおけるセットアップ装飾

@pytest.mark.parametrize(...)
def test_case(...):
    ...

6. よくある誤用と対策

❌ wrapperで戻り値を返さない

→ ✅ 必ず return func(...) を忘れずに書くことで、オリジナルの機能を壊さない


❌ functools.wraps を使わないことで関数情報が破損

→ ✅ デバッグ性を高めるために、@wraps は常に付けるのが基本


❌ 本体と無関係な機能を過剰に追加

→ ✅ デコレーターは「責務の周辺」に限定し、1つのデコレーターに1つの機能


結語

Decoratorパターンとは、“拡張を内包し、振る舞いを優しく包み込む”設計技法である。

  • 実装本体を変えずに、新しい機能を動的に付加できる
  • Pythonにおいては関数・クラスレベルでの応用が可能で、テスト性・柔軟性・再利用性を劇的に高める
  • 設計の明瞭さと実装の疎結合性を両立できる、非常に実用的な構造

Pythonicとは、“変えずに加える”柔らかさを設計することであり、
Decoratorパターンはその柔軟な包容力を、コード全体に優雅に浸透させる。



フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link