데코레이터는 다른 함수를 입력받고 확장되거나 변경된 동작을 가진 새로운 함수를 반환하는 함수입니다. 로그 기록, 권한 검사, 캐싱, 실행 시간 등의 용도로 자주 사용됩니다.
데코레이터는 클로저(closure) 또는 __call__ 메서드를 구현한 클래스를 사용하여 구현됩니다. 전형적인 구문:
# 간단한 데코레이터 def simple_decorator(func): def wrapper(*args, **kwargs): print("함수 호출 전") result = func(*args, **kwargs) print("함수 호출 후") return result return wrapper @simple_decorator def my_func(): print("주 함수") my_func()
그 결과는 다음과 같습니다:
함수 호출 전
주 함수
함수 호출 후
주요 이점 — 기본 비즈니스 로직 외부에서 반복되는 논리를 캡슐화하는 것입니다.
미세한 점:
functools.wraps를 사용하지 않으면 원래 함수의 이름과 docstring이 손실됩니다.질문: "내가 함수를 데코레이터로 장식한 후 __name__을 통해 함수의 이름을 얻으려고 하면 무엇을 보게 되고 원래 이름을 어떻게 유지합니까?"
답변:
기본적으로 이름은 래퍼의 이름(보통 wrapper)으로 변경됩니다. 원래 메타데이터를 유지하려면 functools.wraps를 사용하십시오:
import functools def dec(f): @functools.wraps(f) def wrapper(*args): return f(*args) return wrapper @dec def foo(): pass print(foo.__name__) # 'foo'를 출력, 'wrapper'가 아님
이야기
대규모 자원 자동화에서 데코레이터로 함수들을 감싼 후, 테스트 함수 이름으로 반사(reflection)를 사용하는 자동 테스트 시스템이 망가졌습니다. 문제는 functools.wraps가 없었던 것입니다.
이야기
로그 기록을 추가하는 데코레이터는 다양한 시그니처를 가진 함수들을 지원하지 않았습니다. *args, **kwargs를 사용하지 않았기 때문입니다. 일부 함수는 조용히 실패했습니다.
이야기
REST API에서 인증을 처리하는 프로젝트에서 개발자가 매개변수를 가진 데코레이터를 구현했지만, 함수를 제대로 중첩하지 않았습니다(세 번의 중첩 대신 두 번의 중첩이었습니다). 결과적으로 데코레이터는 매개변수를 받을 수 없었습니다.