デコレータは関数だけでなく、クラスとしても実装できます。状態を保持したい場合や、複雑なロジックを整理したい場合に有効です。
基本構造
クラスベースのデコレータは __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 層ネストよりも構造が明確になることがあります。