한양과학기술고등학교

에듀테크 프로젝트 대시보드

데코레이터

@ wrapper functools.wraps closure

함수를 감싸 기능을 덧붙이는 문법 설탕. @deco 는 f = deco(f) 와 같습니다.

1. 가장 단순한 형태

def announce(func):
    def wrapper(*args, **kwargs):
        print(f"{func.__name__} 호출")
        result = func(*args, **kwargs)
        print(f"{func.__name__} 완료")
        return result
    return wrapper

@announce
def add(a, b):
    return a + b

add(2, 3)      # "add 호출" / "add 완료" / => 5

2. functools.wraps

from functools import wraps

def announce(func):
    @wraps(func)                # 원본의 __name__, __doc__ 유지
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

# wraps 없으면 add.__name__ 이 'wrapper' 가 되어 디버깅·문서화가 깨짐

3. 실용 예 — 실행 시간 측정

import time
from functools import wraps

def timed(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            elapsed = time.perf_counter() - start
            print(f"{func.__name__}: {elapsed:.4f}s")
    return wrapper

@timed
def slow():
    time.sleep(1)

4. 인자를 받는 데코레이터

def repeat(times):                 # 데코레이터 팩토리
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def hello():
    print("hi")
# hello = repeat(3)(hello)  와 동일

5. 표준 라이브러리 데코레이터

데코레이터용도
@functools.lru_cache(maxsize=None)결과 캐싱 (메모이제이션)
@functools.cache무제한 캐시 (3.9+)
@property메서드를 속성처럼
@staticmethod / @classmethod메서드 종류 지정
@dataclasses.dataclass보일러플레이트 자동 생성
@functools.wraps래퍼에 원본 메타데이터 복사
from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(100)        # 캐시 덕분에 즉시

6. 여러 데코레이터

@timed
@lru_cache
def compute(n): ...

# 아래에서 위로 적용: compute = timed(lru_cache(compute))
"There should be one — and preferably only one — obvious way to do it"