[Python] Decorator
데코레이터(Decorator)
데코레이터(Decorator)란 사전적 의미로는 “장식가” 또는 “인테리어 디자이너” 등의 의미를 가지고 있습니다. 이름 그대로, 자신의 방을 예쁜 벽지나 커튼으로 장식을 하듯이, 기존의 코드에 여러가지 기능을 추가하는 파이썬 구문이라고 생각하면 된다.
즉, 함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다. 함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다. 일반적으로는 함수의 전처리나 후처리에 대한 필요가 있을 때 사용한다. 또한 데코레이터를 이용해, 반복을 줄일고 메소드나 함수의 책임을 확장한다. 본인의 경우에는 파이썬을 학습하면서 새로 정의한 함수의 기능을 측정할 때 사용했다. 즉, 해당 함수가 시작될 때 시간을 측정하고 함수의 기능이 모두 종료된 후에 시간을 측정하여 그 값의 차를 구해 함수의 총 수행시간을 계산하여 출력하는 데코레이터를 만들어 활용했었다.
데코레이터의 구조
함수로 만드는 데코레이터는 일반적으로 아래와 같은 구조를 가지고 있다.
def out_func(func): # 기능을 추가할 함수를 인자로
def inner_func(*args, **kwargs):
return func(*args, **kwargs)
return inner_func
def decorator(func):
def wrapper(*args, **kwargs):
print('전처리')
print(func(*args, **kwargs))
print('후처리')
return wrapper
@decorator
def example():
return '함수'
example()
'''''''''
전처리
함수
후처리
'''''''''
또한, 클래스로 만드는 데코레이터 구조는 다음과 같다.
class Decorator:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
return self.function(*args, **kwargs)
class Decorator:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
print('전처리')
print(self.function(*args, **kwargs))
print('후처리')
@Decorator
def example():
return '클래스'
example()
'''''''''
전처리
클래스
후처리
'''''''''
함수형 데코레이터 사용 예제
import datetime
def datetime_decorator(func):
def decorated():
print datetime.datetime.now()
func()
print datetime.datetime.now()
return decorated
@datetime_decorator
def main_function_1():
print "MAIN FUNCTION 1 START"
@datetime_decorator
def main_function_2():
print "MAIN FUNCTION 2 START"
@datetime_decorator
def main_function_3():
print "MAIN FUNCTION 3 START"
- 먼저 decorator 역할을 하는 함수를 정의하고, 이 함수에서 decorator가 적용될 함수를 인자로 받는다. python 은 함수의 인자로 다른 함수를 받을 수 있다는 특징을 이용하는 것이다.
- decorator 역할을 하는 함수 내부에 또 한번 함수를 선언(nested function)하여 여기에 추가적인 작업(시간 출력) 을 선언해 주는 것이다.
- nested 함수를 return 해주면 된다.
클래스형 데코레이터 사용 예제
import datetime
class DatetimeDecorator:
def __init__(self, f):
self.func = f
def __call__(self, *args, **kwargs):
print datetime.datetime.now()
self.func(*args, **kwargs)
print datetime.datetime.now()
class MainClass:
@DatetimeDecorator
def main_function_1():
print "MAIN FUNCTION 1 START"
@DatetimeDecorator
def main_function_2():
print "MAIN FUNCTION 2 START"
@DatetimeDecorator
def main_function_3():
print "MAIN FUNCTION 3 START"
my = MainClass()
my.main_function_1()
my.main_function_2()
my.main_function_3()
decorator를 class로 사용하고 싶다면 위와 같이 __call__ 함수로 decorator 형식을 정의해 주면된다. class의 __call__ 함수로 정의해주는게 nested 함수 형식으로 정의한 것 보다 더 깔끔해 보인다.
데코레이터 실 사용 예제
학생들의 평균을 계산하는 코드를 일단 아래와 같이 작성하였다.
score = [(100, 100), (95, 90), (55, 60), (75, 80), (70, 70)]
def get_avg(score:list):
# 리스트와 반복문을 사용
# 학생별 평균
for index, point in enumerate(score):
print(f'{index+1}번, 평균 : {sum(point)/len(point):.1f}')
만약 위의 함수가 교내 교직원만 사용되어야 하는 함수라서 교직원의 코드를 입력해야만 사용할 수 있다고 가정한다면 교내에서만 사용하는 코드를 확인하고 실행할 수 있는 데코레이터 함수를 선언할 수 있다.
def need_code(func):
def wrapper(*args, **kwargs):
code = "1234"
input_pw = input("비밀번호를 입력하세요 : ")
if input_code == code:
result = func(*args, **kwargs)
else:
result = "잘못된 코드입니다.
return result
return wrapper
위에서 설명했듯 데코레이터를 선언하고 사용하면 결과적으로 wrapper 함수가 실행되는 것이다. input으로 코드를 입력받고 코드가 맞으면 데코레이터를 함수로 전달한 func 죽, get_avg() 함수를 실행하고, 코드가 맞지 않으면 "잘못된 코드입니다." 를 반환한다.