Decorator란 이미 작성된 함수에 새로운 기능을 추가하여 함수를 확장시키는 개념으로, 일반적으로 함수의 전처리나 후처리에 대한 필요가 있을 경우 사용한다. 함수명 위에 @데코레이터로 사용할 함수 이름을 적어주면 된다. 예시를 보자.
만약, 서버에서 사용자에 관련된 메서드를 만든다고 하면 다음과 같은 순서로 처리하게 될 것이다.
# 1. 권한체크 (전처리)
# 2. 요청에 대한 처리 (본 로직)
# 3. 로그 수집 또는 오류 처리 (후처리)
def create_user(*args):
if check_auth():
# main logic
log()
def update_user(*args):
if check_auth():
# main logic
log()
def delete_user(*args):
if check_auth():
# main logic
log()
예시 코드를 보면 같은 작업이 전, 후로 반복되는 것을 볼 수 있다.
여기에 Docorator를 적용해보면 다음과 같다.
def deco(func):
def wrapper(*args):
if auth_check():
func(*args)
log()
return wrapper
@deco
def create_user(*args):
# main logic
@deco
def update_user(*args):
# main logic
@deco
def delete_user(*args):
# main logic
Decorator를 사용한다고 성능상 나아지는 것은 없다.
시스템이 커질수록 관리하기 편하고, 소스코드 또한 주요 로직만 보니 가독성이 좋아지는 장점이 있다. @method_name을 사용하지 않아도, 다음과 같이 사용할 수도 있다.
def deco(func):
def wrapper(*args):
if auth_check():
func(*args)
log()
return wrapper
def create_user(*args):
# main logic
deco_create_user = deco(create_user(*args))
만약 Decorator를 두개 사용한다면 어떻게 될까?
def star(func):
def inner(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent
def printer(msg):
print(msg)
printer("Hello")
결과는 다음과 같다. @star는 @percent를 데코레이트 하고, @percent는 printer를 데코레이트 한다.
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
피보나치 수열을 구하는 함수를 만들어 보면 다음과 같이 메모이제이션을 통해 반복 계산을 줄일 수 있다.
res = {}
def fibo(n):
if n < 3:
return 1
if res.get(n):
return res.get(n)
return fibo(n - 1) + fibo(n - 2)
위의 코드에서 Decorator를 적용해보면, res 변수를 Decorator 안으로 넣을 수 있고, 가독성을 증가시킬 수 있다.
def fibo_deco(func):
res = {}
def inner(*args):
n = args[0]
if res.get(n):
return res.get(n)
r = func(n)
res[n] = r
return r
return inner
@fibo_deco
def fibo(n):
if n < 3:
return 1
return fibo(n - 1) + fibo(n - 2)
Decorator를 사용할 때 주의해야 할 점으로는 데코레이터를 사용한 함수는 속성이 사라진다는 것이다.
def deco(func):
def inner(*args):
func(*args)
return inner
@deco
def test():
'''
테스트 함수
'''
pass
print(test.__name__) # inner
print(test.__doc__) # None
test함수의 __name__과 __doc__을 출력해보면 __name__은 inner, __doc__는 None으로 출력되는 것을 확인할 수 있다. 이러한 부분을 방지하기 위해서 wraps를 사용할 수 있다.
from functools import wraps
def deco(func):
@wraps(func)
def inner(*args):
func(*args)
return inner
@deco
def test():
'''
테스트 함수
'''
pass
print(test.__name__) # test
print(test.__doc__) # 테스트 함수
wraps를 사용하여 기존 함수의 속성을 유지시킬 수 있다는 것을 확인할 수 있다.
'🐍Python | Django' 카테고리의 다른 글
[Python] main 함수 (0) | 2022.04.26 |
---|---|
[Python] Class와 상속(Inheritance) (0) | 2022.04.19 |
[Python] Under Score(_)? (0) | 2022.04.14 |
[Python] String join()/split() (0) | 2022.02.07 |
[Python] strftime과 strptime (0) | 2021.08.25 |