Decorate 개념
decorator : 실내장식가
데커레이터(Decorator)는 하나의 함수를 취해서 또 다른 함수를 반환하는 함수입니다.
@decorator
def function():
print "what is decorator?"
한마디로 얘기하자면, 대상 함수를 wrapping 하고, 이 wrapping 된 함수의 앞뒤에 추가적으로 꾸며질 구문 들을 정의해서 손쉽게 재사용 가능하게 해주는 것입니다....?(자세한 설명은 밑에서)
Decorator는 어떤 경우에 씁니까?
코딩을 하다 보면 종종 이런 경우가 있습니다.
한 구문이 있고, 여기에 부가적인 구문을 추가하고 싶을때가 있습니다.
그리고 이 부가적인 구문을 반복해서 사용하고 싶은 경우도 있습니다.
이때 부가적인(그리고 반복적인) 작업을 decorator 로 선언해서 자유롭게 사용이 가능합니다.
처음엔 이해가 잘 안가지만, 막상 사용하다 보면 굉장히 쉽다는 것을 느낄 수 있습니다.
아래에서 간단한 예시와 함께 데코레이터 사용 시의 장점을 알아보겠습니다.
def main_function():
print "MAIN FUNCTION START"
만약 해당 함수가 출력되기 전과 후에 날짜와 시간을 출력해야 한다고 합시다.
import datetime
def main_function():
print datetime.datetime.now()
print "MAIN FUNCTION START"
print datetime.datetime.now()
위와 같이 작성할 수 있습니다.
하지만 위와 같은 작업을 여러번 반복해야 한다면?
import datetime
def main_function_1():
print datetime.datetime.now()
print "MAIN FUNCTION 1 START"
print datetime.datetime.now()
def main_function_2():
print datetime.datetime.now()
print "MAIN FUNCTION 2 START"
print datetime.datetime.now()
def main_function_3():
print datetime.datetime.now()
print "MAIN FUNCTION 3 START"
print datetime.datetime.now()
.... X 100번
반복되는 구문이 많다보니 소스가 지저분해져 가독성이 떨어집니다.
함수 실행 전과 후에 시간을 출력하는 간단한 구문이 이런데 실제 사용될 때는 소스의 상태가 심각해질 것입니다.
이럴때 아래와 같이 데코레이터를 사용할 수 있다.
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"
..... X 100번
decorator 함수를 재사용 함으로써, main 함수에 대한 가독성과 직관성이 훨씬 좋아진 것을 볼 수 있습니다다.
그리고 같은 패턴을 여러번 사용하더라고 간단히 @를 붙이면 끝이므로 사용도 간편합니다.
아래에서는 데코레이터의 사용 예시와 함께 설명해보겠습니다.
데코레이터 예제1 : 수동으로 활용하기
# 데코레이터 예제
def decorator_function(original_function): #1, #4
def wrapper_function(): #5 #8
return original_function() #9
return wrapper_function #6
def display(): #2, #10
print("display 함수가 실행됐습니다") #11
decorated_display = decorator_function(display) #3 display 함수를 전달
decorated_display() #7
출력결과 :
display 함수가 실행됐습니다
display 함수가 실행됐습니다
None
위 코드는 #
의 순서를 따른다.
#3
: decorated_display라는 변수는 decorated_function 함수의 리턴값을 할당받는다.#6
: return값은 wrapper_function이라는 함수다.#3
변수는 결국 함수를 실행시킬 수 있는 함수다.#7
: 변수가 함수니까 괄호를 붙여 실행시켜보자.#8
,#9
: return 값으로 original_function을 돌려준다.- 여기서 closure 함수의 개념이 쓰인다.
- 바깥 함수(decorator_function)에서 전달받은
original_function
을 기억하고 있다.
#10
,#11
: 여기서 비로소 함수를 실행하고, print문을 실행한다.
display함수에서 반환값이 없기 때문에 함수 자체의 반환값은 None입니다!
# 데코레이터 예제
def decorator_function(original_function):
def wrapper_function():
print("{} 함수가 호출되기 전입니다.".format(original_function.__name__))
return original_function()
return wrapper_function
def display_1():
print("display_1 함수가 실행됐습니다.")
def display_2():
print("display_2 함수가 실행됐습니다.")
display_1 = decorator_function(display_1)
display_2 = decorator_function(display_2)
display_1()
print("")
display_2()
wrapper 함수를 통해서 간단하게 기능 추가를 할 수 있습니다.
데코레이터 예제2 : @
심볼 활용하기
# @ 심볼 활용하기
def decorator_function(original_function):
def wrapper_function():
print("{} 함수가 호출되기 전입니다.".format(original_function.__name__))
return original_function()
return wrapper_function
@decorator_function
def display_1():
print("display_1 함수가 실행됐습니다.")
@decorator_function
def display_2():
print("display_2 함수가 실행됐습니다.")
# display_1 = decorator_function(display_1)
# display_2 = decorator_function(display_2)
출력 결과:
display_1() # 변수에 함수형 리턴값 할당 없이 바로 함수 호출이 가능하다.
display_2() # 변수에 함수형 리턴값 할당 없이 바로 함수 호출이 가능하다.
데코레이터 예제3 : 인자가 전달되는 함수는 어떻게 데코레이팅 할까?
예제3.1 : 위치인자와 키워드인자 활용하기
def decorator_function(original_function):
def wrapper_function():
print("{} 함수가 호출되기 전입니다.".format(original_function.__name__))
return original_function()
return wrapper_function
@decorator_function
def display_1():
print("display_1 함수가 실행됐습니다.")
@decorator_function
def display_info(name, age): # 위 예제와 다르게 인자가 전달된다.
print("display_info( {}, {} ) 함수가 실행됐습니다.".format(name, age))
# display_1 = decorator_function(display_1)
# display_2 = decorator_function(display_2)
display_1()
display_info('김아무개', 37)
출력 결과:
TypeError: wrapper_function() takes 0 positional arguments but 2 were given
display_1 함수는 정상적으로 출력되지만, display_info
함수는 타입에러가 생겼습니다.
wrapper 함수에 위치인자와 키워드인자를 넣어주면 해결됩니다.
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print("{} 함수가 호출되기 전입니다.".format(original_function.__name__))
return original_function(*args, **kwargs)
return wrapper_function
예제3.2 : 위치인자와 키워드인자 활용하기
다음 예제는 Introducing Python
이라는 책에서 발췌한 예제이다.
여기서는 위치인자와 키워드인자까지 활용했습니다.
def document_it(func):
def new_function(*args, **kwargs):
print('Running function : ', func.__name__)
print('Positional arguments : ', args)
print('Keyword arugments :' , kwargs)
result = func(*args, **kwargs)
print('Result : ', result)
return result
return new_function
def add_inits(a, b):
return a + b
def div_inits(a,b):
return a / b
add = document_it(add_inits)
div = document_it(div_inits)
print(add(5,3))
print("")
print(div(5,3))
출력 결과:
Running function : add_inits
Positional arguments : (5, 3)
Keyword arugments : {}
Result : 8
8
Running function : div_inits
Positional arguments : (5, 3)
Keyword arugments : {}
Result : 1.6666666666666667
1.6666666666666667
데코레이터 예제4 : 인자가 전달되는 함수는 어떻게 데코레이팅 할까?
데코레이터에서 파라미터를 입력받고 싶다면, 조금 복잡한데 함수를 2개로 만들어서 이용합니다.
반환값인 return을 잘 맞추어서 사용해야 합니다.
from functools import wraps
def print_a(file_name):
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(file_name)
return func(*args, **kwargs)
return wrapper
return decorate
@print_a("hahaha") #데코레이터에 "hahaha"를 입력받았다!
def print_c():
print("c")
print_c()
결과:
hahaha
c
데코레이터 예제5 : class 형태로 decorator를 사용해 보기.
decorator를 class로 사용하고 싶다면 아래와 같이 call 함수로 decorator 형식을 정의해 주면됩니다.
class의 call 함수로 정의해주는게 nested(중첩) 함수 형식으로 정의한 것 보다 더 깔끔해 보입니다.
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())
print('\n')
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()
그래서, 데코레이터를 언제 쓰는데?
- 로그를 남길 때
- 유저의 로그인 상태를 확인하여 로그인 페이지로 리다이렉트(redirect)
- 프로그램 성능을 위한 테스트
이렇게 파이썬 데코레이터란 무엇이고, 어떻게 사용해야 하는지에 대해서 알아보았습니다.
안에 wrapper함수 뒤에 코드를 정의하면, 함수 실행을 마치고 실행되야 하는 코드도 정의할 수 있습니다.
데코레이터의 활용범위는 생각보다 넓을 듯 합니다.
또한 함수별로 필수적으로 실행해야 하는 기능을 한 번에 정의할 수 있어 편리하고, 특히 유지보수 하기에 용이합니다.
그리고 이건 파이썬 데코레이터를 작성하는 방법을 배워야하는 이유 5가지라는 제목의 글이다.
www.hanbit.co.kr/media/channel/view.html?cms_code=CMS5689111564
참조:
https://whatisthenext.tistory.com/113
https://tariat.tistory.com/846
https://bluese05.tistory.com/30
http://schoolofweb.net/blog/posts/파이썬-데코레이터-decorator/
'python' 카테고리의 다른 글
[python] 이터레이터와 제너레이터 (0) | 2021.02.20 |
---|---|
[python] lambda expression(람다 표현식) (0) | 2021.02.19 |
[python] Closure(클로저 ) (0) | 2021.02.17 |
[python] class, instance, Method (0) | 2021.02.11 |
객체 지향 프로그래밍(OOP)이란? (0) | 2021.02.01 |