알아야 할 것들..
단순히 말하자면 파이썬 데코레이터는 어떤 함수를 감싸서(wrap), 그 함수의 행동을 수정하는 함수인데, 파이썬에서는 Syntactic Sugar를 더해 데코레이터를 타겟 함수에 쉽게 적용하도록 만들어 준다.
def my_decorator(func):
def my_wrapper():
# Wrapper는 데코레이터가 받은 함수의 행동 또는 인자를 수정한다.
# 이 경우 원 함수를 두 번 실행한다.
func()
func()
# 데코레이터는 Wrapper를 리턴한다.
return my_wrapper
# 데코레이터가 적용된 함수는 사실 my_wrapper(say_hello)와 같다.
@my_decorator
def say_hello():
print("Hello?")
>> say_hello() # 이 실행은 사실 my_wrapper(say_hello)()와 같다.
"Hello?"
"Hello?"
원 함수에 입력 인자가 있는 경우 데코레이터의 wrapper 함수도 같은 인자를 받게 설정해야 한다.
하지만 이 경우 데코레이터의 타겟 함수가 같은 수의 인자를 받는 함수만 수정할 수 있게끔 제한된다.
해결책은 *args
, **kwargs
인자를 wrapper 함수에 적용하는 것.
def my_decorator(func):
def my_wrapper(*args, **kwargs):
# wrapper가 받은 임의의 인자를 그대로 원 함수에 전달한다.
func(*args, **kwargs)
func(*args, **kwargs)
return my_wrapper
@my_decorator
def say_hello():
print("Hello?")
@my_decorator
def say_name(name):
print(f"Hello {name}?")
>> say_hello()
"Hello?"
"Hello?"
>> say_name("Jun")
"Hello Jun?"
"Hello Jun?"
단순하게, wrapper가 원함수를 실행하고 결과를 같이 리턴해 주면 된다.
def my_decorator(func):
def my_wrapper(*args, **kwargs):
# wrapper가 받은 임의의 인자를 그대로 원 함수에 전달한다.
func(*args, **kwargs)
return func(*args, **kwargs)
return my_wrapper
함수가 데코레이터에 의해 wrapped 된 경우 해당 함수의 메타정보는 더이상 그 함수를 가리키지 못한다.
이 경우 functool
빌트인 라이브러리에 정의된 데코레이터 @functool.wraps
를 이용해서 데코레이터 정의에서 wrapper를 데코레이팅 해주자.
예를들어,
def my_decorator(func):
def my_wrapper(*args, **kwargs):
func(*args, **kwargs)
func(*args, **kwargs)
return my_wrapper
import functools
def better_decorator(func):
@functools.wraps(func)
def my_wrapper(*args, **kwargs):
func(*args, **kwargs)
func(*args, **kwargs)
return my_wrapper
@my_decorator
def say_name(name):
print(f"Hello {name}?")
@my_decorator2
def say_name2(name):
print(f"Hello {name}?")
>> say_name.__name__
'my_wrapper'
>> say_name2.__name__
'say_name2'