데코레이터 함수는 우선 장식을 해줄 func을 파라미터로 전달받는다.
그리고 decorated라는 함수를 내부에 만들어주고 이를 리턴해준다.
decorated 함수 안에서는 전달받은 함수의 앞 뒤에 실행할 코드를 작성해준다.
def hello_decorator(func):
def decorated():
print('Hello!')
func()
print('Nice to meet you!')
return decorated
@hello_decorator
def hello():
print("I'm Giung")
hello()
Hello! I'm Giung Nice to meet you!
이처럼 'I'm Giung'만 출력하는 'hello()' 함수가 데코레이터로 인해 앞 뒤에 문장이 추가된다.
데코레이터는 언제 사용할까? -> 여러 함수가 중복되는 작업을 수행할 경우, 코드의 가독성 및 재사용성을 높이기 위해 사용한다.
예시)
import time
def task1():
time.sleep(.5)
print('Task 1 has been completed.')
def task2():
time.sleep(1)
print('Task 2 has been completed.')
def task3():
time.sleep(1.5)
print('Task 3 has been completed.')
위 사례처럼 각기 다른 동작을 수행하고, 소요 시간이 다른 세 임의의 함수가 있다고 가정.
이 때 개발자는 각각의 함수가 실행되는데 소요되는 시간이 얼마나 걸리는지 확인하고자 한다.
datetime 모듈을 사용해서 코드를 단순 반복한다면,
import time
from datetime import datetime
def task1():
start = datetime.now()
time.sleep(.5)
print('Task 1 has been completed.')
end = datetime.now()
print('timecost', end - start)
def task2():
start = datetime.now()
time.sleep(1)
print('Task 2 has been completed.')
end = datetime.now()
print('timecost', end - start)
def task3():
start = datetime.now()
time.sleep(1.5)
print('Task 3 has been completed.')
end = datetime.now()
print('timecost', end - start)
task1()
task2()
task3()
Task 1 has been completed. timecost 0:00:00.507962 Task 2 has been completed. timecost 0:00:01.004957 Task 3 has been completed. timecost 0:00:01.513311
수행하고자 하는 동작 앞, 뒤에 현재 시간을 재는 코드가 추가됨. 그리고 수행을 완료한 이후 소요 시간을 출력. 코드는 잘 실행되지만, 서로 다른 세 함수에 동일한 코드가 삽입되어 가독성이 좋지 않다.
데코레이터를 사용하여서 이를 해결할 수 있다.
from datetime import datetime
def timecost(func):
def decorated():
start = datetime.now()
func()
end = datetime.now()
print('timecost: ', end - start)
return decorated
import time
@timecost
def task1():
time.sleep(.5)
print('Task 1 has been completed.')
@timecost
def task2():
time.sleep(1)
print('Task 2 has been completed.')
@timecost
def task3():
time.sleep(1.5)
print('Task 3 has been completed.')
task1()
task2()
task3()
Task 1 has been completed. timecost: 0:00:00.513039 Task 2 has been completed. timecost: 0:00:01.014544 Task 3 has been completed. timecost: 0:00:01.511803
코드의 가독성이 훨씬 올라갔다.
함수 안에 함수가 들어있는 모양이 보기 좋지 않다면,
데코레이터를 함수가 아니라 클래스로 만들 수도 있다.
from datetime import datetime
class Timecost():
def __init__(self, func):
self.func = func
def __call__(self):
start = datetime.now()
self.func()
end = datetime.now()
print('timecost', end - start)
import time
@Timecost
def task1():
time.sleep(.5)
print('Task 1 has been completed.')
@Timecost
def task2():
time.sleep(1)
print('Task 2 has been completed.')
@Timecost
def task3():
time.sleep(1.5)
print('Task 3 has been completed.')
task1()
task2()
task3()
Task 1 has been completed. timecost 0:00:00.501778 Task 2 has been completed. timecost 0:00:01.005172 Task 3 has been completed. timecost 0:00:01.508368
/
/
/
실제 프레임워크(Ex. Flask)들에서는 데코레이터를 어떻게 사용할까?
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
이는 파이썬 웹 어플리케이션 프레임워크인 플라스크에서 API를 작성하는 코드.
사용자는 자신이 수행하고자하는 동작 (Hello World 리턴)을 입력한 뒤에 데코레이터를 달아준다.
플라스크 앱의 route 데코레이터 함수는 이제 / 라는 url로 http 요청이 들어오면 사용자가 작성한 함수를 실행한다.
그리고 이를 http response로 만들어서 요청자에게 반환시켜준다.
from celery import Celery
app = Celery('tasks', broker='pyamqp://guest@localhost//')
@app.task
def add(x, y):
return x + y
다음은 파이썬 비동기 분산처리 프레임워크 셀러리에서 비동기 처리를 해주는 코드. (구체적인 내용은 몰라도 상관없다.)
단순히 x와 y를 더해주는 함수에 app.task 함수를 데코레이터로 붙여준다.
그러면 브로커에 연결된 샐러리 앱이 브로커 내부의 큐에 테스크가 쌓이는 지를 확인하고
테스트가 있으면 가져와서 계산하고 다시 반환을 어쩌고 저쩌고 하는
듣기만 해도 현기증 나는 작업들을 해주는 멋진 비동기 함수로 재탄생한다다.
즉, 이러한 프레임워크의 강력한 기능들을 데코레이터를 통해 손쉽게 가져다 쓸 수 있는 것이다.
데코레이터가 무엇이고 실제 개발에선 어떻게 사용되는지 알아보았다.
---3줄 요약---
- 데코레이터는 함수를 파라미터로 전달 받으며, 그 함수 앞 뒤에 다른 동작들을 수행해준다.
- 코드의 재사용성, 가독성을 높이기 위해 사용한다.
- 프레임워크를 사용할 때 많이 볼 수 있다.
/
/
/
댓글