클로저는 바깥 함수의 지역 이름을 기억하는 내부 함수다. 데코레이터는 함수를 받아 다른 호출 가능 객체를 반환한다.

from functools import wraps
from time import perf_counter

def timed(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            print(func.__name__, perf_counter() - start)
    return wrapper

`@timed`는 정의 시점에 `func = timed(func)`를 실행한 것과 같다. 인자를 받는 데코레이터는 데코레이터를 만드는 함수까지 한 겹 더 필요하다.

루프에서 람다나 내부 함수를 만들면 바깥 이름의 현재 값이 호출 시점에 조회된다. 모두 마지막 값을 보는 늦은 바인딩이 생길 수 있다. `lambda x=i: x`처럼 기본 인자에 현재 값을 저장하거나 별도 팩토리 함수로 범위를 만든다.

데코레이터가 많아지면 실제 호출 경로가 숨는다. 인증·로깅 같은 횡단 관심사에 제한하고 순서를 문서화한다.

Python 17. 데코레이터와 클로저의 핵심 흐름도
그림. 데코레이터는 호출 경계를 감싸되 원래 함수 정보를 보존한다.