데코레이터는 함수를 감싸는 형태로 구성되어, 기존 함수를 수정하지 않으면서 추가 기능을 구현할 때 사용합니다. 이전에 배운 클로저를 기반으로 동작합니다.
# 데코레이터 없이 — 함수를 직접 감싸는 방식
def my_decorator(func):
def wrapper():
print("함수 실행 전")
func()
print("함수 실행 후")
return wrapper
def hello():
print("Hello!")
hello = my_decorator(hello) # 감싸기
hello()
# 함수 실행 전
# Hello!
# 함수 실행 후
@ 문법을 사용하면 위 코드를 훨씬 간결하게 표현할 수 있습니다.
def my_decorator(func):
def wrapper():
print("함수 실행 전")
func()
print("함수 실행 후")
return wrapper
@my_decorator # hello = my_decorator(hello)와 동일
def hello():
print("Hello!")
hello()
# 함수 실행 전
# Hello!
# 함수 실행 후
원본 함수의 매개변수와 반환값을 그대로 처리하려면 wrapper 함수에도 매개변수를 지정하고 반환값을 전달해줘야 합니다.
def decorator(func):
def wrapper(x, y): # 원본 함수와 동일한 매개변수
print(f"입력값: {x}, {y}")
result = func(x, y)
print(f"반환값: {result}")
return result # 반환값 전달
return wrapper
@decorator
def add(x, y):
return x + y
add(3, 5)
# 입력값: 3, 5
# 반환값: 8
어떤 함수에든 범용으로 적용할 수 있는 데코레이터를 만들려면 *args와 **kwargs를 활용합니다.
def universal_decorator(func):
def wrapper(*args, **kwargs): # 어떤 인수든 받을 수 있음
print(f"함수명: {func.__name__}")
print(f"위치 인수: {args}")
print(f"키워드 인수: {kwargs}")
result = func(*args, **kwargs)
return result
return wrapper
@universal_decorator
def introduce(name, age, city="서울"):
print(f"이름: {name}, 나이: {age}, 도시: {city}")
introduce("홍길동", 25, city="부산")
# 함수명: introduce
# 위치 인수: ('홍길동', 25)
# 키워드 인수: {'city': '부산'}
# 이름: 홍길동, 나이: 25, 도시: 부산
데코레이터 자체에 매개변수를 전달하려면 데코레이터를 반환하는 함수를 한 겹 더 감쌉니다.
def repeat(count): # 데코레이터 매개변수
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(count):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3) # 함수를 3번 반복 실행
def hello():
print("Hello!")
hello()
# Hello!
# Hello!
# Hello!
함수 대신 클래스로 데코레이터를 만들 수도 있습니다. __init__에서 함수를 받고, __call__에서 추가 기능을 구현합니다.
class MyDecorator:
def __init__(self, func):
self.func = func # 원본 함수 저장
def __call__(self, *args, **kwargs): # 호출 시 실행
print("함수 실행 전")
result = self.func(*args, **kwargs)
print("함수 실행 후")
return result
@MyDecorator
def hello():
print("Hello!")
hello()
# 함수 실행 전
# Hello!
# 함수 실행 후
매개변수가 있는 클래스 데코레이터는 __init__에서 매개변수를 받고, __call__에서 함수를 받아 wrapper를 반환합니다.
class Repeat:
def __init__(self, count): # 데코레이터 매개변수 받기
self.count = count
def __call__(self, func): # 원본 함수 받기
def wrapper(*args, **kwargs):
for _ in range(self.count):
result = func(*args, **kwargs)
return result
return wrapper
@Repeat(3)
def hello():
print("Hello!")
hello()
# Hello!
# Hello!
# Hello!
데코레이터는 함수의 실행 시간을 측정하거나 호출 정보를 로깅하는 디버깅 목적으로 자주 활용됩니다. 기존 함수를 전혀 수정하지 않아도 되기 때문에 특히 유용합니다.
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"[{func.__name__}] 실행 시간: {end - start:.4f}초")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
print("작업 완료!")
slow_function()
# 작업 완료!
# [slow_function] 실행 시간: 1.0012초
| 데코레이터 유형 | 구현 방식 | 핵심 포인트 |
|---|---|---|
| 기본 데코레이터 | wrapper 함수 | @데코레이터명 |
| 매개변수·반환값 처리 | wrapper(*args, **kwargs) | return result 필수 |
| 매개변수 있는 데코레이터 | 함수 3중 중첩 | @데코레이터(값) |
| 클래스 데코레이터 | __init__ + __call__ | __call__이 wrapper 역할 |
| 클래스 + 매개변수 | __init__(매개변수) + __call__(함수) | 두 단계로 분리 |