기본 개념
Python에서 데코레이터를 사용하면 함수가 다른 함수로 래핑됩니다. 하지만, 래핑 과정에서 원래 함수의 이름(__name__), 문서 문자열(__doc__), 기타 메타데이터가 사라질 수 있습니다.
데코레이터로 인한 문제:
from functools import wraps
def my_decorator(func):
def wrapper(*args, **kwargs):
"""Wrapper docstring"""
print("Before the function call")
result = func(*args, **kwargs)
print("After the function call")
return result
return wrapper
@my_decorator
def my_function():
"""Original function docstring"""
print("The function is running")
print(my_function.__name__) # 출력: wrapper
print(my_function.__doc__) # 출력: Wrapper docstring
위 코드에서 @my_decorator를 사용한 결과, my_function의 이름과 문서 문자열이 래퍼 함수(wrapper)로 덮어씌워졌습니다.
@wraps로 해결
@wraps를 사용하면, 데코레이터가 원래 함수의 메타데이터를 유지하도록 설정할 수 있습니다.
개선된 코드:
from functools import wraps
def my_decorator(func):
@wraps(func) # 원래 함수의 메타데이터를 유지
def wrapper(*args, **kwargs):
"""Wrapper docstring"""
print("Before the function call")
result = func(*args, **kwargs)
print("After the function call")
return result
return wrapper
@my_decorator
def my_function():
"""Original function docstring"""
print("The function is running")
print(my_function.__name__) # 출력: my_function
print(my_function.__doc__) # 출력: Original function docstring
동작 원리
@wraps는 내부적으로 functools.update_wrapper를 호출하여 아래와 같은 메타데이터를 래핑된 함수에 복사합니다:
- __name__: 함수 이름
- __doc__: 문서 문자열
- __module__: 모듈 정보
- __annotations__: 함수의 주석
- 기타 메타데이터
사용 예시
1. 간단한 데코레이터:
from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__} with arguments {args} and {kwargs}")
return func(*args, **kwargs)
return wrapper
@logger
def add(a, b):
"""Adds two numbers"""
return a + b
print(add.__name__) # 출력: add
print(add.__doc__) # 출력: Adds two numbers
2. 정규 데코레이터에서 사용:
여러 데코레이터를 중첩하거나 라이브러리용 데코레이터를 구현할 때도 유용합니다.
요약
- @wraps는 데코레이터 내부에서 사용하여 원래 함수의 메타데이터를 유지합니다.
- 이를 사용하면 디버깅 및 문서화가 쉬워지고, 데코레이터로 인해 발생하는 부작용을 방지할 수 있습니다.
- 항상 커스텀 데코레이터 작성 시 @wraps를 사용하는 것이 좋은 습관입니다.
'Python > study' 카테고리의 다른 글
__init__ (0) | 2025.02.19 |
---|---|
unittest 모듈 (0) | 2024.12.08 |
@staticmethod (0) | 2024.12.08 |
ast.literal_eval() (0) | 2024.12.08 |
__str__와 __repr__ (0) | 2024.12.08 |