PHpullh
학습 라이브러리/Python/데코레이터 — 함수를 감싸는 함수

PYTHON · 함수

데코레이터 — 함수를 감싸는 함수

데코레이터로 횡단 관심사(로깅, 캐싱, 인증 등)를 분리합니다.

함수중급decoratorfunctools.wrapstimerretrymemoize

핵심 설명

데코레이터로 횡단 관심사(로깅, 캐싱, 인증 등)를 분리합니다.

Python code

import functools
import time

# 기본 데코레이터 구조
def timer(func):
    @functools.wraps(func)  # 원본 메타데이터 보존
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__}: {elapsed:.4f}초")
        return result
    return wrapper

# 파라미터를 받는 데코레이터 (팩토리)
def retry(times: int = 3, delay: float = 0.5):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"시도 {attempt}/{times} 실패: {e}")
                    if attempt < times:
                        time.sleep(delay)
            raise RuntimeError(f"{times}번 모두 실패")
        return wrapper
    return decorator

# 클래스 기반 데코레이터
class Memoize:
    def __init__(self, func):
        self.func = func
        self.cache = {}
        functools.update_wrapper(self, func)

    def __call__(self, *args):
        if args not in self.cache:
            self.cache[args] = self.func(*args)
        return self.cache[args]

@timer
@retry(times=2, delay=0.1)
def fetch_data(url: str) -> str:
    return f"data from {url}"

@Memoize
def fib(n):
    return n if n < 2 else fib(n-1) + fib(n-2)

print(fib(35))   # 빠르게 계산

학습 팁

@functools.wraps(func)를 반드시 붙이세요. 없으면 wrapper.__name__, wrapper.__doc__이 원본 함수의 것을 잃어버립니다.

주의할 점

데코레이터를 여러 개 쌓으면 아래에서 위로 적용됩니다. @A @B def f()A(B(f))입니다.

자주 묻는 질문

데코레이터 — 함수를 감싸는 함수란 무엇인가요?

데코레이터로 횡단 관심사(로깅, 캐싱, 인증 등)를 분리합니다.

데코레이터 — 함수를 감싸는 함수 학습 시 주의할 점은 무엇인가요?

데코레이터를 여러 개 쌓으면 아래에서 위로 적용됩니다. @A @B def f() 는 A(B(f)) 입니다.