Decorator는 무엇인가? 


- 다른 function의 기능을 조작하여 새로운 function을 만드는 것.

- 이 방법은 코드를 더욱 간결하게 만들며, 더욱 Pythonic 한 코드를 만들 수 있다

- 이러한 형태의 일종의 코드 Refactoring 및 중복 줄이기는 소프트웨어 공학에서 매우 중요하다!


Decoration을 안 한 초보 파이썬 코더의 코드


# 기존의 코드를 사용 안 하고, b_function()을 새롭게 정의함.
# 이렇게 하면 문제가, my foul smell을 삭제하고 싶으면, 함수 2개에서 모두 삭제해야한다. 
# 코드의 중복이 생김.
 
def a_function_requiring_decoration(): 
    print("I am the function which needs some decoration to remove my foul smell") 
 
def b_function(): 
    print("I am doing some boring work before executing a_func()") 
    print("I am the function which needs some decoration to remove my foul smell")   
    print("I am doing some boring work after executing a_func()")
 
b_function()

- 이 방법의 문제점은 코드의 중복이 생겨 수정이 필요할 시에 두 함수 모두를 수정해야한다는 것이다. 


간단한 Decoration의 구현 


# 아래 함수를 기능을 추가해서 decoration 해주는 함수 def a_new_decorator(a_func):


# 함수 안에 함수를 정의하고 함수를 리턴한다 def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()")   return wrapTheFunction   # 이 함수를 decoration (기능을 추가) 하고 싶음 def a_function_requiring_decoration(): print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) a_function_requiring_decoration()

결과


I am doing some boring work before executing a_func()

I am the function which needs some decoration to remove my foul smell

I am doing some boring work after executing a_func()


- 이를 해결하는 방법이 바로 decoration 이다. 

- a_new_decorator 함수에 a_function_requiring_decoration 함수를 넘기는 방법을 통해 내용이 한 번만 쓰이게 된다. 

- 이를 통해 코드의 중복을 줄일 수 있다.



@ 키워드를 통한 decoration


# @를 붙임으로써 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 이걸 안 해도 된다. @a_new_decorator def a_function_requiring_decoration(): """Hey you! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell")   a_function_requiring_decoration()   # 근데 함수명이 이상하게 나옴. wrapTheFunction print(a_function_requiring_decoration.__name__)

- @ 키워드를 통해 a_function_requiring_decoration를 재정의 하지 않아도 된다.

- @ [함수명] 을 decoration 하고 싶은 함수 위에 붙여주면 된다. 

- 근데 함수 명이 wrapTheFunction 으로 decoration 한 함수의 이름이 그대로 나오게 된다. 

- 이를 해결하기 위해 wraps 를 이용한다.


# wraps를 이용해 함수명이 제대로 나오게 할 수 있음
from functools import wraps
# 최종적인 decorator의 일반적인 형태
# a_function_requiring_decoration을 a_new_decorator로 decorating 한다는 것이다. 이 때 decorate 할 함수는 a_func에 지정하고 이를 wraps로 받아서 그 아래 함수로 decoration 함
 
def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction(): 
        print("I am doing some boring work before executing a_func()") 
        a_func() 
        print("I am doing some boring work after executing a_func()")
 
    return wrapTheFunction
 
@a_new_decorator 
def a_function_requiring_decoration(): 
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to " "remove my foul smell")
 
a_function_requiring_decoration() 
 
print(a_function_requiring_decoration.__name__)  # a_function_requiring_decoration


- 이렇게 decorator에 wraps를 붙여주면, 그 함수를 decoration 해주는 함수로 인식을 하게 된다. 

- 함수명도 기존의 함수 명인 a_function_requiring_decoration 을 따르게 된다.


Decoration 활용의 좋은 예 - Authentication


authentication_check라는 함수를 만들고 이곳에서는 웹어플리케이션서의 사용자 인증을 체크한다고 하자. 만약 다른 함수를 실행할 때, 그 함수의 위에다가 위에다가 @authentication_check 만 붙이면, authentication을 알아서 해주게 된다. 즉, Authentication - function 실행 순으로 알아서 만들어 준다.  이것이 좋은 점은 각 함수마다 authentication check를 안해도되고, authentication check logic을 딱 한 번만 쓰면 된다. 이런건 Java에서는 보통 상속을 이용해서 하는데, python에서는 decorator로 할 수 있다.


""" Use case : Authorization Now let’s take a look at the areas where decorators really shine and their usage makes something really easy to manage. Decorators can help to check whether someone is authorized to use an endpoint in a web application. They are extensively used in Flask web framework and Django. Here is an example to employ decorator based authentication:   """   # decorator 함수 def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated

- 위 require_auth 함수는 어떤 함수 f를 받아서 그 전에 authorization 과정을 수행해주는 decorator이다. 



참고 - Intermediate Python