@데코레이터란?
함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다.
함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다 .
- 일반적으로 함수의 전처리나 후처리에 대한 필요가 있을때 사용을 한다.
- 또한 데코레이터를 이용해, 반복을 줄이고 메소드나 함수의 책임을 확장한다.
logger_byeol 함수에 inner_func 함수의 기능을 추가해 만든 decorated_func 함수는 데코레이터를 사용할 때와 결과는 같다.
# 데코레이터 func 정의
def outer_func(func):
def inner_func():
print("[Log] start")
func()
print("[Log] end")
return inner_func
# 데코레이터를 사용할 함수 정의
def logger_byeol():
print("Byeol login")
decorated_func = outer_func(logger_byeol)
decorated_func()
[Log] start
Byeol login
[Log] end
위와 같은 기능을 @데코레이터로 작성한다면
# 데코레이터 func 정의
def outer_func(func):
def inner_func():
print("[Log] start")
func()
print("[Log] end")
return inner_func
@outer_func
def logger_byeol2():
print("Byeol login")
logger_byeol2()
[Log] start
Byeol login
[Log] end
위에서 만든 함수는 파라미터가 없었다.
그렇다면 파라미터가 있는 함수에 @데코레이터를 적용하려면 어떻게 해야할까?
중첩함수(Nested Function)에 동일한 파라미터를 선언한다.
# 데코레이터 func 정의
def outer_func(func):
def inner_func(num1, num2):
if num2 == 0:
print("유효성 검사: 0으로는 나눌 수 없습니다.")
return
func(num1, num2)
return inner_func
@outer_func
def divide(num1, num2):
print(num1 / num2)
divide(10, 0)
유효성 검사: 0으로는 나눌 수 없습니다.
그런데 여러 함수에 적용하려고 하면 파라미터를 계속 바꿔주어야 하는데 불편하다.
파라미터와 관계없이 모든 함수에 적용하는 @데코레이터를 만들려면?
- @데코레이터의 내부함수 파라미터를 (*args, **kwargs)로 작성한다.
*args는 가변 인자를 위한 변수이다. 즉, 함수의 인자를 몇개 받을지 모르는 경우에 사용한다. 튜플 형태로 값을 저장한다.
**kwargs가 *args와의 차이점은 딕셔너리 형태로 값을 저장하고, 파라미터 명을 같이 보낼 수 있다는 점이다.
한 함수에 여러 @데코레이터를 적용할 수 도 있다.
@decorator1의 func에 @decorator2 def test()를 인자로 받기 때문에 아래와 같이 출력이 된다.
def decorator1(func):
def wrapper():
print("decorator1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("decorator2")
func()
return wrapper
@decorator1
@decorator2
def test():
print("test")
test()
decorator1
decorator2
test
다음과 같이 응용할 수 있다.
def mark_bold(func):
def wrapper(*args, **kwargs):
return '<b>' + func(*args, **kwargs) + '</b>'
return wrapper
@mark_bold
def get_text(string):
return string
print(get_text('술 마시고 싶다.'))
<b>술 마시고 싶다.</b>
@데코레이터를 클래스의 메소드에 적용하려면?
클래스 method의 첫 파라미터가 self이므로 이 부분을 @데코레이터 작성 시에 포함시키면 된다.
def h1_tag(func):
def wrapper(self, *args, **kwargs):
return f"<h1>{func(self, *args, **kwargs)}</h1>"
return wrapper
class Champion:
def __init__(self, name, level):
self.name = name
self.level = level
@h1_tag
def get_info(self):
return self.name + " " + str(self.level)
# 데코레이터 적용 결과 확인
draven = Champion("드레이븐", 1)
print(draven.get_info())
<h1>드레이븐 1</h1>
그럼 @데코레이터 함수에 파라미터를 선언하고 싶다면?
def decorator1(num):
def outer_wraaper(func):
def inner_wrapper(*args, **kwargs):
print(f'decorator의 파라미터: {num}')
return func(*args, **kwargs)
return inner_wrapper
return outer_wraaper
@decorator1(1)
def test():
print('test')
test()
decorator의 파라미터: 1
test