Decorator 는 chain of functions 즉, 여러개의 함수가 자동으로 연속적으로 호출되게하는 기능을 한다.
Decorator로 장식할 수 있는 함수는 중첩 함수(nested function)을 리턴하는 함수만 decorator 함수로 사용될 수 있다.
다음은 데코레이터로 둘러쌓인 함수가 특정 조건을 만족할 때만 수행되게 하는 예제이다.
# n번 (진행되는 순서) / n번 (return 값이 나오는 순서)
def is_valid_user(func): # 2번
is_valid = True
def wrapper(): # 4번
if is_valid:
return func() # 5번 / 2번
# return 이 아니라 func() 만 호출하면
# say_hi() 의 return 값은 None 이 되어버린다.
else:
return
return wrapper # 3번 / 3번
@is_valid_user
def say_hi(): # 6번
return 'hi' # 7번 / 1번
hello = say_hi() # 1번 / 4번
print(hello) # 'hi'
decorator 로 둘러쌓여있는 say_hi()
함수를 호출하면 파이썬은 이를 is_valid_user(say_hi)
로 해석하여 호출해준다.
is_valid_user(func)
의 인자로 say_hi()
함수 자체가 들어간다.
is_valid_user()
는 중첩함수인 wrapper
를 리턴한다.
wrapper()
함수 안에서 조건문 비교를 한다.
wrapper
함수를 호출하기 전 is_valid
의 값은 True
였으므로 첫 번째 is_valid:
블럭으로 들어간다.
func()
을 return 한다.
fucn()
의 return 값 'hi'
는 wrapper()
의 return 값이며, 최종적으로 데코레이터로 둘러쌓인say_hi()
의 return 값이다.
따라서 변수 hello
의 값이 'hi'
가 된다.
하지만 이 때 wrapper()
함수 내부에서 func()
을 return 하지않고 그저 호출만 한다면,
wrapper()
함수의 return 값은 None
이 되므로 is_valid_user()
함수의 return 값 또한 None
이 되기 때문에 최종적으로 데코레이터로 둘러쌓인 say_hi()
의 return 값은 None
이 된다.hello
의 값이 None
이 된다.이렇듯 decorator 는 특정 조건 하에서만 함수가 실행되야만 할 때 편하게 사용하기 위해 작성하는 함수이다.
또한 하나의 함수에 여러개의 decorator 를 중첩해서 사용할 수 있다.
다음 코드는 tornado 웹 프레임워크를 사용해 작성한 코드의 일부이며, 주식코드를 async 하게 가져오는 내용이다.
물론 이미 만들어져있는 데코레이터를 가져다 쓴 것이지만, 실용성은 매우 높았다.
예를들어 @tornado.web.authenticated
데코레이터는 유저가 로그인 되어있는지 체크한 뒤 로그인 되어있을 때만 특정 함수를 실행한다.
또한 @run_on_executor
데코레이터는 sync 한 특정 함수를 설정했던 thread pool executor 위에서 async 하게 실행해준다.
이렇게 데코레이터를 사용해본 적은 있지만 사실 직접 데코레이터를 만들어보고 자세히 공부해보는 것은 처음이다. 앞으로 직접 유용한 데코레이터를 직접 만들어 볼 수 있을 것 같다.