Decorator - (2) 클래스 만들기
Decorator - (1) 함수 만들기 에서 Decorator를 함수로 만들어봤었다. 이번 시간엔 Decorator를 클래스로 만들어 볼 것인데, 먼저 그 전에 다음을 알아야 한다.
class HelloWorld():
def __init_(self):
pass
간단한 클래스이다. 이 클래스를 함수처럼 실행시켜 버리면 어떻게 될까?
helloworld = HelloWorld()
helloworld()
이렇게 실행하면
TypeError: 'HelloWorld' object is not callable
물론 오류가 뜰 것이다. 왜냐하면 HelloWorld
는 클래스이므로 함수처럼 호출할 수 없기 때문이다.
하지만 이 클래스를 함수처럼 호출할 수 있는 방법이 있다.
class HelloWorld():
def __init_(self):
pass
def __call__(self):
print("hello world")
위와 같이 __call__
함수를 클래스에 추가해준다. __call__
함수는 이 클래스의 객체가 함수처럼 호출되면 실행되는 함수이다. 아까의 코드를 다시 실행해보면 'hello world'가 정상적으로 출력될 것이다.
이와 같은 클래스의 기능을 이용하여 Decorator 클래스를 만들어 볼 것이다. 어떤 함수의 실행시간을 측정해주는 Decorator 클래스를 만들어보자.
import time
class Timer():
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
start_time = time.time()
result = self.function(*args, **kwargs)
end_time = time.time()
print("실행시간은 {time}초입니다.".format(time=end_time-start_time))
return result
쉽게 말해서 여기서 __call__(self)
함수가 Decorator 함수에서의 wrapper 함수의 역할을 한다고 생각하면 된다. wrapper 함수에서처럼 Decorator 클래스가 적용될 함수의 parameter를 *args
, **kwargs
가변인자로 받는다.
이제 이 Decorator 클래스를 특정 함수에 적용해보자.
@Timer
def print_hello(name):
print("hello, "+ name)
print_hello('python')
특정함수 위에다가 Decorator 클래스를 선언해주면, 이 함수가 실행될 때 자동으로 Timer
의 객체가 생성되고 __init__
함수의 parameter로 이 함수 자신이 들어가게 된다. 그리고 객체가 함수처럼 호출됐으므로 아까와 같이 __call__
함수가 실행되어 최종 결과를 출력하는 것이다. 나는 print_hello()
라는 함수만 실행했을 뿐이지만 사실 그 속에는 이렇게도 많은 일들이 일어나고 있다.
이 예시만 보면 굳이 Decorator 클래스를 써야 되나 라는 의문이 든다. Decorator 함수를 쓰면 더 간단한 게 사실이기 때문이다. 하지만 이렇게 클래스로 구현해놓으면 Decorator를 좀 더 구조화하기 쉬어진다.
예시를 보며 확인해보자.
class Tagify():
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
tagified_p = self.tagify('p', self.function(*args, **kwargs))
tagified_b = self.tagify('b', tagified_p)
tagified_i = self.tagify('i', tagified_b)
return tagified_i
def tagify(self, tag, text):
return "<{tag}>{text}</{tag}>".format(tag=tag, text=text)
@Tagify
def set_text(text):
return text
set_text('python') # <i><b><p>python</p></b></i>
문자열을 i, b, p 태그로 감싸주는 Decorator 클래스이다. 태그를 감싸주는 작업이 계속 반복되므로 이 작업을 tagify
라는 함수로 따로 빼놓은 것을 볼 수 있다. 지금도 그렇게 복잡한 Decorator은 아니지만 Decorator가 복잡해지면 복잡해질수록 클래스로 구조화해 놓는 것이 빛을 발한다.