Python 데코레이터 패턴 모음
재시도(retry), 캐싱(cache), 실행 시간 측정(timer), 권한 확인(auth) 등 실무에서 자주 쓰이는 Python 데코레이터 패턴들입니다.
import time
import functools
import logging
from typing import Callable, Any, Dict
logger = logging.getLogger(__name__)
def timer(func: Callable) -> Callable:
"""함수의 실행 시간을 측정합니다."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
duration = end_time - start_time
print(f"[{func.__name__}] executed in {duration:.4f}s")
return result
return wrapper
def retry(retries: int = 3, delay: float = 1.0) -> Callable:
"""실패 시 지정된 횟수만큼 재시도합니다."""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for i in range(retries):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
logger.warning(f"Retry {i+1}/{retries} for {func.__name__} after error: {e}")
time.sleep(delay)
raise last_exception
return wrapper
return decorator
def cache(func: Callable) -> Callable:
"""함수의 결과값을 메모리에 캐싱합니다."""
memo: Dict[tuple, Any] = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
if key not in memo:
memo[key] = func(*args, **kwargs)
return memo[key]
return wrapper
def require_auth(role: str = "user") -> Callable:
"""가상의 권한 확인을 수행합니다."""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(user_context: dict, *args, **kwargs):
if user_context.get("role") != role:
raise PermissionError(f"Required role: {role}, got: {user_context.get('role')}")
return func(user_context, *args, **kwargs)
return wrapper
return decorator
# 사용 예시
@timer
@retry(retries=2)
@cache
def expensive_operation(n: int) -> int:
time.sleep(0.5)
return n * 2
@require_auth(role="admin")
def delete_item(user: dict, item_id: int):
print(f"Item {item_id} deleted by {user['name']}")
if __name__ == "__main__":
print(expensive_operation(10))
print(expensive_operation(10)) # 캐시 히트
admin_user = {"name": "Alice", "role": "admin"}
delete_item(admin_user, 123)