데코레이터 패턴
- 기존 코드를 수정하지 않고 동적으로 기능을 확장할수 있게 해주는 패턴
- 함수를 인자로 받아 감싼 뒤, 새로운 함수를 반환하는 함수로 구현한다.
왜?
- 코드의 쉬운 재사용
- 관심사의 분리
- 쉬운 기능 확장
예시
def logger(func):
# arg, kwargs는 func에 전달되는 패러미터 들이다.
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}...")
result = func(*args, **kwargs)
print(f"{func.__name__} finished.")
return result
return wrapper
@logger
def say_hello():
print("Hello!")
say_hello()
1. 인자를 받지 않는 데코레이터
import time
def timeit(func):
def wrapper(*args, **kwargs):
current = time.time()
result = func(*args, **kwargs)
after = time.time()
print(f'{after - current} second took')
return result
return wrapper
@timeit
def test1():
ans = 0
for i in range(100000000):
ans += i
print(ans % 10**9)
test1()
2. 인자를 받는 데코레이터
def repeat(n: int):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet():
print('Hello World!')
greet()
- 최상단에 인자를 받는 부분을 포함하여 nesting 한다.
클래스 기반 데코레이터
class RequireRole:
def __init__(self, role):
self.role = role
def __call__(self, func):
def wrapper(*args, **kwargs):
user = kwargs.get("user") or (args[0] if args else None)
if user != self.role:
print(f"Access denied, need to be {self.role}")
return
return func(*args, **kwargs)
return wrapper
@RequireRole("admin")
def delete_database(user):
print(f"{user} deleted the DB")
__init__ 에서 직접 parameter를 받을수 있다.
- 데코레이터의 구현은
__call__에서 한다.
데코레이터에서 받아올수 있는 기타 정보들
| 속성 | 설명 |
|---|
__name__ | 함수 이름 |
__doc__ | 함수 설명 문자열 |
__module__ | 함수가 정의된 모듈 이름 |
__annotations__ | 타입 힌트 |
__defaults__ | 기본 인자 값 |
functools.wraps | 위 정보들을 감싸는 wrapper에 그대로 복사 |
def logger(func):
def wrapper(*args, **kwargs):
print(f"Function: {func.__name__}")
print(f"Module: {func.__module__}")
print(f"Doc: {func.__doc__}")
return func(*args, **kwargs)
return wrapper
@logger
def greet(name: str = "world"):
"""Prints a greeting"""
print(f"Hello, {name}!")