3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

概要

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パターンはその柔軟な包容力を、コード全体に優雅に浸透させる。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?