데코레이터(Decorator)란?
함수/메서드의 기능을 확장하거나 변경해 주는 특별한 문법
파이썬 코드를 공부하다 보면, '@' 골뱅이가 붙은 특이한 이름을 보신 적이 있으실 겁니다. 파이썬에서 골뱅이(@)는 데코레이터임을 의미하는 약속어 같은 표시입니다.
데코레이터란 직역 하면 장식하는 주체로, 함수나 메서드에 적용되어 기능을 추가, 확장, 변경하는 역할을 합니다. 데코레이터는 함수를 인자로 받고, 또 다른 함수를 반환하는 고차함수(higher-order function)입니다.
데코레이터를 사용하면 코드의 길이를 단축할 수 있고, 개념을 아는 사람들은 직관적으로 파악할 수 있어서 가독성을 높여주는 기능을 합니다. 뿐만 아니라, 함수의 핵심 기능에 집중할 수 있도록 도움을 주기도 합니다.
데코레이터 기초: 직접 구현해 보기 (인자가 없는 경우)
데코레이터, 이것만 기억하자: "함수를 입력받아, 함수를 반환"
우선 인자가 없는 두 개의 함수로 데코레이터의 기초를 실습해 보겠습니다. 첫 번째 함수는 "Good_night" 함수입니다. "좋은 꿈 꾸렴."이라는 문자열을 출력합니다.
def Good_night():
print("좋은 꿈 꾸렴")
Good_night()
# 위 코드의 결과값
좋은 꿈 꾸렴
두 번째 함수는 "Love_you"라는 함수입니다. "잘 자요, 내 사랑."을 출력하고 인자로 받은 함수를 실행한 다음, "꿈에서 봐요."를 출력합니다. "잘 자요, 내 사랑" → "좋은 꿈 꾸렴." → "꿈에서 봐요."를 순차적으로 출력하기 위해서 다음과 같이 함수를 인자로 함수를 호출할 수 있습니다.
def Love_you(fn): # 함수 fn을 인자로 받는다.
print("잘자요, 내 사랑.")
fn() # 인자로 받은 함수 fn을 호출
print("꿈에서 봐요.")
Love_you(Good_night)
# 위 코드의 결과값
잘자요, 내 사랑.
좋은 꿈 꾸렴
꿈에서 봐요.
위의 "Love_you"라는 함수는 함수를 인자로 받습니다. Love_you 함수를 조금 수정하여 함수를 반환하도록 바꿔주면 데코레이터로서 활용할 수 있습니다. 기억합시다. "데코레이터는 함수를 인자로 받아서, 함수를 반환한다."
Love_you에서 수행되는 모든 기능을 print_fn이라는 함수로 정의했습니다. 그리고 이를 return 하도록 코드를 수정했습니다. 데코레이터는 적용하고자 하는 함수/메서드 위에 @함수/메서드명으로 정의합니다.
Good_night 함수 위에 Love_you 함수를 데코레이터로 정의해서 사용해 보겠습니다. Love_you는 함수를 인자로 받고, 함수를 반환하는 구조이므로 데코레이터로 활용할 수 있습니다.
def Love_you(fn): # 함수 fn을 인자로 받는다.
def print_fn():
print("잘자요, 내 사랑.")
fn() # 인자로 받은 함수 fn을 호출
print("꿈에서 봐요.")
return print_fn
@Love_you
def Good_night():
print("좋은 꿈 꾸렴")
Good_night()
# 위 코드의 실행 결과
잘자요, 내 사랑.
좋은 꿈 꾸렴
꿈에서 봐요.
데코레이터 심화: 데코레이터로 인자를 함께 전달
*args, **kwargs를 기억하자.
두 숫자를 더한 값을 출력하는 함수와, 그 함수의 결과값을 보기 좋게 꾸며주는 함수 두 개를 만들어보겠습니다. 꾸며주는 함수를 데코레이터로 사용하기 위해서는 더해주는 함수를 인자로 받아야 합니다. 이때, 더해주는 함수를 실행하기 위해서는 두 개의 인자가 필요하기 때문에 데코레이터로 인자를 함께 전달해야 합니다.
데코레이터로 인자를 함께 전달하기 위해서는 *args와 **kwargs를 활용하면 됩니다. 데코레이터가 return 할 함수에 *args와 **kwargs를 인자로 넣어주어야 하며, 그 외 나머지 기본적인 구조는 동일합니다.
def deco_answer(fn):
def decorate_your_answer(*args, **kwargs):
print(f"{fn.__name__} 함수를 시작합니다.")
print("계산 중 ... ")
print(f"실행결과: {fn(*args, **kwargs)} 입니다.")
return decorate_your_answer
@deco_answer
def add_value(a,b):
return a+b
add_value(5,10)
# 위 코드의 실행 결과
add_value 함수를 시작합니다.
계산 중 ...
실행결과: 15 입니다.
데코레이터 심화 2: 데코레이터로부터 값 return 받기 & @wraps
데코레이터로부터 값을 return 받고 싶을 때는, 데코레이터가 함수를 return 하기 전에 return 명령어를 한번 더 사용해 주면 됩니다. 이렇게 되면, 데코레이터도 실행되고 데코레이터로부터 값도 return 받을 수 있습니다.
아래 예시를 보면 이해가 되실 겁니다.
add_value 함수를 데코레이터로 전달하여, 처리하고 그 결과값을 'return_from_decorator' 변수에 할당했습니다. return_from_decorator에는 데코레이터에서 return 하도록 설정한 문자열이 들어가 있는 것을 확인할 수 있습니다.
from functools import wraps
def deco_answer(fn):
@wraps(deco_answer)
def decorate_your_answer(*args, **kwargs):
print(f"{fn.__name__} 함수를 시작합니다.")
print("계산 중 ... ")
print(f"실행결과: {fn(*args, **kwargs)} 입니다.")
return "데코레이터로부터 return합니다." # 데코레이터로부터 결과값 반환받기
return decorate_your_answer
@deco_answer
def add_value(a,b):
return a+b
return_from_decorator = add_value(5,10)
print(return_from_decorator)
# 위 코드의 결과값
add_value 함수를 시작합니다.
계산 중 ...
실행결과: 15 입니다.
데코레이터로부터 return합니다.
참고로 데코레이터는 함수를 주고받기 때문에, 이 과정에서 원래 함수의 메타 정보가 데코레이터의 메타 정보로 대체될 가능성이 있습니다. 이 가능성을 미연에 차단하기 위해서는 @wraps 데코레이터를 선언해 주면 됩니다. (@wraps 데코레이터는 functools의 wraps 모듈에 있습니다.)
'Python > 기초' 카테고리의 다른 글
[파이썬/Python] Class에 특별한 메서드 (0) | 2023.07.12 |
---|---|
[파이썬/Python] 정적 메서드, 클래스 메서드, 인스턴스 메서드 (0) | 2023.07.12 |
[파이썬/Python] Class 심화: 상속에 대한 모든 것 (0) | 2023.07.11 |
[파이썬/Python] Class의 정의와 사용법(feat. 함수와의 차이) (0) | 2023.07.11 |
[파이썬/Python] 인코딩(encoding)에 대한 깔끔한 정리 (1) | 2023.07.09 |
댓글