Class-based decorators use __call__ and can maintain state.
1import functools2import time34class RateLimiter:5 def __init__(self, max_calls, period):6 self.max_calls = max_calls7 self.period = period8 self.calls = []910 def __call__(self, func):11 @functools.wraps(func)12 def wrapper(*args, **kwargs):13 now = time.time()14 # Remove old calls15 self.calls = [t for t in self.calls if now - t < self.period]1617 if len(self.calls) >= self.max_calls:18 raise Exception("Rate limit exceeded")1920 self.calls.append(now)21 return func(*args, **kwargs)2223 wrapper.reset = self.reset24 return wrapper2526 def reset(self):27 self.calls.clear()2829@RateLimiter(max_calls=5, period=60)30def api_call():31 return "Response"3233# Works34for _ in range(5):35 api_call() # OK3637# api_call() # Exception: Rate limit exceeded38api_call.reset() # Reset counter39api_call() # OK again
Benefits: