Python のクラスベースデコレータ

デコレータは関数だけでなく、クラスとしても実装できます。状態を保持したい場合や、複雑なロジックを整理したい場合に有効です。

基本構造

クラスベースのデコレータは __init__ で関数を受け取り、__call__ でラッパー処理を行います。

from functools import wraps

class CountCalls:
    def __init__(self, func):
        wraps(func)(self)
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__}{self.count} 回呼ばれました")
        return self.func(*args, **kwargs)

使い方は関数デコレータと同じです。

@CountCalls
def say_hello():
    print("Hello!")

say_hello()  # say_hello は 1 回呼ばれました / Hello!
say_hello()  # say_hello は 2 回呼ばれました / Hello!

クラスなので、self.count のように状態を自然に保持できます。

引数を取るクラスデコレータ

引数を取る場合は、__init__ で引数を受け取り、__call__ で関数を受け取ります。

class Repeat:
    def __init__(self, n):
        self.n = n
    
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(self.n):
                result = func(*args, **kwargs)
            return result
        return wrapper

@Repeat(3)
def greet():
    print("Hi!")

@Repeat(3) でインスタンスが生成され、その __call__ にデコレート対象の関数が渡されます。関数ベースの 3 層ネストよりも構造が明確になることがあります。