이전 강좌에서 functools 모듈의 wraps 데코레이터를 사용하면서 지금까지 우리가 배웠던 데코레이터와는 다르게 인자를 사용하고 있는 것을 보았다. 이번 강좌에서는 이와 같이 인자를 받을 수 있는 데코레이터에 대해 알아 보도록 하겠다.
목차
인자를 가지는 데코레이터
지금까지 우리가 만든 add_emoticon 예제는 한가지 이모티콘 밖에 출력 할 수 없었다. 이제 아래 예제 처럼 add_emoticon 함수가 인자를 가질 수 있도록 개선하여 고정된 이모티콘이 아닌 인자로 넘겨 받은 문자열을 출력 할 수 있도록 해보자.
# @add_emoticon # say_hello = add_emoticon(say_hello)
@add_emoticon('^0^') # say_hello = add_emoticon('^0^')(say_hello)
def say_hello(name) :
print(f'hello {name}!')
기존 @add_emoticon
을 사용하면 인터프리터가 say_hello = add_emoticon(say_hello)
와 같은 코드로 변경해준다고 이야기 했다. 똑같은 방식으로 데코레이터에 @add_emoticon('^0^')
와 같이 인자를 추가해주면 컴파일러는 기존 코드에 저 인자 부분을 추가한 say_hello = add_emoticon('^0^')(say_hello)
와 같은 코드로 변경한다.
그런데 코드 모양이 이상해 보인다? 함수를 호출하는데 괄호를 여러번 여닫고 있다. 문법 오류 같아 보이긴 하지만 정상적인 코드 맞다. 여러분의 이해를 돕기위해 위 코드를 풀어서 보도록 하겠다.
# say_hello = add_emoticon('^0^')(say_hello)
decorator = add_emoticon('^0^')
say_hello = decorator(say_hello)
add_emoticon에서 기존 say_hello 대신 이모티콘을 인자로 받아 기존 add_emoticon 과 동일한 역할을 해주는 어떤 함수 객체(여기서는 decorator)를 생성하고, 그 함수 객체를 이용해 기존 add_emoticon이 했던것과 동일한 작업을 처리하고 있다.
위 내용을 토대로 add_emoticon 함수를 하나씩 하나씩 변경해 보도록 하자.
# def add_emoticon(func) :
def add_emoticon(emoticon) : # 기존 함수 객체 대신 이모티콘을 인자로 받도록 수정
@wraps(func)
def inner(*args, **kwargs) :
print('= ̄ω ̄= ', end='')
result = func(*args, **kwargs)
return result
return inner
기존 함수 객체 대신 이모티콘을 인자로 받을 수 있도록 수정했다. 그럼 이제는 기존 add_emoticon과 동일한 역할을 하는 어떤 함수 객체를 리턴해야 할 차례다. 아래 예제에서는 decorator라는 함수객체를 만들어 리턴했다.
def add_emoticon(emoticon) :
def decorator(func) : # 기존 add_emoticon과 동일한 역할을 하는 함수 객체
@wraps(func)
def inner(*args, **kwargs) :
print('= ̄ω ̄= ', end='')
result = func(*args, **kwargs)
return result
return inner
return decorator
이제 고정된 이모티콘 대신 데코레이터의 인자로 넘겨진 이모티콘을 대신 출력 할 수 있도록 변경해보자. 하는 김에 인자가 없는 경우 기본 이모티콘을 출력 할 수 있도록 emoticon에 디폴트 값도 추가하자.
def add_emoticon(emoticon = '^_^') : # emoticon에 디폴트 인자 추가
def decorator(func) :
@wraps(func)
def inner(*args, **kwargs) :
print(emoticon, ' ', end='') # 고정 이모티콘 대신 인자로 넘어온 이모티콘 출력
result = func(*args, **kwargs)
return result
return inner
return decorator
축하한다. 이제 인자를 가지는 데코레이터가 완성 되었다. 이제 다시 처음으로 돌아가 say_hello와 say_hi 함수에 데코레이터를 적용해보자.
from functools import wraps
def add_emoticon(emoticon='^_^') :
def decorator(func) :
@wraps(func)
def inner(*args, **kwargs) :
print(emoticon, ' ', end='')
result = func(*args, **kwargs)
return result
return inner
return decorator
@add_emoticon('^o^') # 커스텀 이모티콘을 사용한 hello
def say_hello(name) :
print(f'hello {name}!')
@add_emoticon() # 디폴트 이모티콘을 사용한 hi
def say_hi() :
print('hi')
say_hello('jason') # ^o^ hello jason!
say_hi() # ^_^ hi
위 예제를 실행하면 데코레이터의 인자에 따라 이모티콘을 출력하는 hello와 hi 메시지를 볼 수 있다.
마치며
이상으로 데코레이터에 인자를 사용 할 수 있는 방법에 대해 살펴 보았다. 본 포스트에서는 설명을 위해 극히 간단한 예제를 사용했지만, 데코레이터에 인자를 사용한다는 복잡 다양한 상황에서 별도의 데코레이터들을 계속 만들지 않아도 되게 해주는 강력한 기능이므로 꼭 숙지하고 넘어 가도록 하자.
다음 강좌 데코레이터의 활용에서는 데코레이터 강좌 시리즈의 마지막으로써 지금까지 배웠던 것들을 종합적으로 사용해 보는 시간을 갖도록 하겠다.