들어가며
Python에서 데코레이터는 함수의 동작을 런타임에 수정할 수 있는 강력한 기능입니다. 이 글에서는 데코레이터의 기본 개념부터 고급 사용법까지 단계별로 살펴보겠습니다.
1. 중첩 함수
데코레이터를 이해하기 전에, 먼저 함수 내에 정의된 함수(중첩 함수)의 개념을 알아야 합니다. 다음과 같은 상황을 고려해 봅시다:
기존 함수 A가 있을 때, 함수 A의 기능을 변경하지 않으면서 새로운 기능을 추가하고 싶다면 어떻게 해야 할까요?
def original_function():
print("original result")
# 원래 함수를 변수에 저장
temp = original_function
# 새로운 기능을 하는 함수 정의
def enhanced_function():
print("additional feature")
temp() # 원래 함수 호출
# 원래 변수에 새 함수 대입
original_function = enhanced_function
위 코드에서 볼 수 있듯이, original_function이라는 이름이 가리키는 객체가发生了变化했습니다. 처음에는 원래 함수를 가리키고 있었지만, 마지막 줄之后就变成了 새로운 함수를 가리키게 됩니다.
2. 데코레이터 기본
2.1 단순 데코레이터
데코레이터는 다른 함수를 반환하는 함수입니다. 다음 예제를 살펴보세요:
def decorator(func):
print("decorator code executed")
def wrapper():
print("before calling")
func()
print("after calling")
return wrapper
@decorator
def say_hello():
print("hello!")
실행 결과를 확인하면, 데코레이터 코드가 함수 호출 전에 먼저 실행되는 것을 볼 수 있습니다. 이는 @decorator가 적용되는 순간 데코레이터 함수가 실행되기 때문입니다.
실제로 위 코드는 다음과 거의 동일합니다:
def say_hello():
print("hello!")
say_hello = decorator(say_hello)
2.2 매개변수가 있는 함수 데코레이션
데코레이션할 함수에 매개변수가 있는 경우:
def decorator(func):
def wrapper(*args, **kwargs):
print("before execution")
result = func(*args, **kwargs)
print("after execution")
return result
return wrapper
@decorator
def add_numbers(a, b):
return a + b
3. 여러 데코레이터
하나의 함수에 여러 데코레이터를 적용할 수 있습니다:
def decorator_one(func):
def wrapper(*args, **kwargs):
print("decorator_one - before")
result = func(*args, **kwargs)
print("decorator_one - after")
return result
return wrapper
def decorator_two(func):
def wrapper(*args, **kwargs):
print("decorator_two - before")
result = func(*args, **kwargs)
print("decorator_two - after")
return result
return wrapper
@decorator_two
@decorator_one
def greet(name):
print(f"Hello, {name}!")
위 코드에서 @decorator_two @decorator_one는 다음과 같이 동작합니다:
greet = decorator_two(decorator_one(greet))
따라서 실행 순서는:
decorator_two의 wrapper 실행decorator_one의 wrapper 실행- 원래
greet함수 실행
4. 매개변수를 받는 데코레이터
데코레이터 자체에 매개변수를 전달해야 하는 경우, 함수 하나를 추가로 감싸야 합니다:
def decorator_with_args(prefix):
def decorator(func):
def wrapper(*args, **kwargs):
print(f"{prefix}: before calling")
result = func(*args, **kwargs)
print(f"{prefix}: after calling")
return result
return wrapper
return decorator
@decorator_with_args("INFO")
def calculate(x, y):
return x * y
위 코드는 다음 코드와 equivalent합니다:
calculate = decorator_with_args("INFO")(calculate)
실행 과정:
decorator_with_args("INFO")호출 →decorator함수 반환- 반환된
decorator함수에 원래 함수 전달 - 최종적으로 수정된 함수 반환
5. 완전한 데코레이터 정의
실제 프로덕션 코드에서는 데코레이터의 __name__ 属性을 원래 함수의 이름으로 복원해야 합니다. 그렇지 않으면某些 프레임워크에서 함수 시그니처를 사용하는 경우 오류가 발생할 수 있습니다.
import functools
def proper_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("before execution")
result = func(*args, **kwargs)
print("after execution")
return result
return wrapper
functools.wraps는 다음 역할을 합니다:
__name__속성을 원래 함수 이름으로 복원__doc__문서 문자열 보존- 다른 함수 메타데이터 유지
functools.wraps 대신 직접 할당도 가능하지만, 권장하지 않습니다:
# 권장하지 않는 방법
def proper_decorator(func):
def wrapper(*args, **kwargs):
# 기능 구현
return func(*args, **kwargs)
wrapper.__name__ = func.__name__
return wrapper
정리
Python 데코레이터는 함수와 메서드의 동작을 확장하는 elegant한 방법입니다. 로그 기록, 성능 측정, 권한 검사, 캐싱 등 다양한 분야에서 유용하게 활용됩니다. 특히大型 프레임워크에서는 데코레이터를 기반으로 한 기능들이 많이 사용되므로, 이 개념을 잘 이해해 두는 것이 중요합니다.