우리가 개발한 딥러닝 모델을 배포하려면 보통 python의 flask를 주로 사용합니다.
아직 해본 적은 없지만 flask나 django 이용한 모델 배포도 제가 보기엔 꼭 필수적인 부분 같아서 이 부분은 무조건 공부해보고 꼭 정리해서 포스팅할 예정입니다.
어쨌든 flask에서 주로 사용되는 python 문법이 바로 데코레이터(Decorator)입니다.
함수나 class 위에 @{name} 이런 식으로 되어 있는 경우 종종 본 적 있죠? 그게 바로 데코레이터입니다.
뭐 일단 한번 보도록 하죠!
변수나 데이터 구조에 할당이 가능한 객체를 의미합니다. 객체가 있으면 그걸 변수에 담아낼 수 있는 녀석들을 말하죠. 파이썬의 모든 함수들은 First-class object로서 parameter나 return 값으로 활용 가능합니다.
무슨 말인지 복잡하죠? 그냥 아래처럼 사용가능하다는 말입니다.
변수에 담아내는 경우
def plus_one (x) :
return x+1
f = plus_one
f(5)
Output
6
plus_one이라는 함수를 f 라고 하는 변수에 할당 시켰습니다.
Parameter로 활용하는 경우
def being_parameter(x) :
return x+1
lst = [1,2,3,4,5]
list(map(being_parameter, lst))
Output
[2, 3, 4, 5, 6]
map이라고 하는 함수에 Parameter로서 들어갔습니다.
예시를 보니까 별 거 아니죠? 말만 어렵지 그걸 표현한 실제는 그렇게 어렵지는 않습니다.
함수 내에 존재하는 또다른 함수를 말합니다. 진짜 문자 그대로 함수 내에 새로 함수를 정의할 수 있습니다.
def big_function (x) :
def inner_function_square(x) :
return print(x**2)
inner_function_square(x)
big_function(5)
Output
25
호출은 big_function을 했는데 함수 안에 정의한 inner_function_square()라는 함수의 기능이 발현된 것을 확인하실 수 있습니다.
진짜 별 거 아니죠?
그러나 나중 가면 되게 많이 쓰는 구조이기 때문에 이런 형식엔 익숙해지는 것이 좋습니다.
여기서 return을 함수로 지정하면 그걸 closer 라고 하죠.
Decorator는 위와 같이 복잡해질 수 있는 closer를 간단하게 만들어주는 기능입니다.
위에서 기존의 함수에 다른 함수의 기능을 불러올 수 있다는 점을 이용하여 함수를 특별히 수정하지 않은 상태에서 추가적인 기능을 구현해낼 수 있는 것이죠.
def decorator (function) : ## 함수를 객체로 받는다! (First-class Object)
def wrapper() : ## parameter로 들어온 객체를 감싸주는 wrapper 함수 를 집어 넣는다
## 새로운 기능이 추가될 내용을 적으면 된다.
print(function.__name__ , '함수 시작')
function() ## wrapper 내에서 함수 호출
print(function.__name__ , '함수 끝')
return wrapper ## decorator 함수는 결국 내부 함수인 wrapper의 결과를 return
def my_name() :
print('안녕하세요!!!!')
objected_my_name = decorator(my_name) ## 함수를 객체화 (First-class Object)
objected_my_name()
Output
my_name 함수 시작
안녕하세요!!!!
my_name 함수 끝
위의 함수의 순서를 말씀드리면 아래와 같습니다.
1. decorator(function) 함수가 정의된다.
2. my_name() 함수가 정의된다.
3. 정의된 decorator 함수에 정으된 my_name 함수가 parameter로 들어간 객체 objected_my_name 이 정의된다.
4. objected_my_name에 값을 할당하기 위해 함수 decorator가 호출 된다.
------ decorator 내부에서 발생하는 일 ------
5. decorator 내부의 함수 wrapper 함수가 정의된다. 동시에 wrapper 함수가 decorator의 return 값으로 나오게 된다.
6. 이 때, 아직 wrapper 함수 내부의 명령들은 시행되지 않는다.
-----------------------------------------
print(function.__name__ , '함수 시작')
function() ## wrapper 내에서 함수 호출
print(function.__name__ , '함수 끝')
엄청 복잡한 과정을 통해 기존의 my_name 함수를 수정하지 않아도 decorator 함수로 출력 값을 어느 정도 수정할 수 있었습니다.
이렇게 복잡한 일은 Python에서 절대 두 눈 뜨고 가만히 놔둘리가 없습니다. 더 간단하게 함수에 객체 할당이 가능하도록 @decorator 기능을 제공합니다.
def decorator (function) :
def wrapper() :
print(function.__name__ , '함수 시작')
function()
print(function.__name__ , '함수 끝')
return wrapper
@decorator
def my_name() :
print('안녕하세요!!!!')
my_name()
Output
my_name 함수 시작
안녕하세요!!!!
my_name 함수 끝
이제 굳이 함수 안에 함수를 parameter로 지정하지 않아도 @ 한방으로 간단하게 똑같은 기능을 구현이 가능해졌습니다.
이를 조금 응용하면 Decorator 2개를 동시에 활용하는 것도 가능합니다. 적용되는 순서는 위에서 아래입니다.
def decorator1 (function) : ## 함수를 객체로 받는다! (First-class Object)
def wrapper() : ## parameter로 들어온 객체를 감싸주는 wrapper 함수 를 집어 넣는다
## 새로운 기능이 추가될 내용을 적으면 된다.
print('이건 첫번째 Decorator')
function() ## wrapper 내에서 함수 호출
print('이건 첫번째 Decorator')
return wrapper ## decorator 함수는 결국 내부 함수인 wrapper의 결과를 return
def decorator2 (function) :
def wrapper() :
print('이건 두번째 Decorator')
function()
print('이건 두번째 Decorator')
return wrapper
@decorator1
@decorator2
def my_name() :
print('안녕하세요!!!!')
my_name()
Output
이건 첫번째 Decorator
이건 두번째 Decorator
안녕하세요!!!!
이건 두번째 Decorator
이건 첫번째 Decorator
이제 함수 위에 @ 하나 붙었다고 쫄 필요 없겠죠? ㅎㅎ