소개
이 섹션에서는 데코레이터 (decorator) 의 개념을 소개합니다. 이는 고급 주제이므로 여기서는 기본적인 내용만 다룹니다.
로깅 예시
함수를 생각해 봅시다.
def add(x, y):
return x + y
이제, 로깅 (logging) 이 추가된 함수를 생각해 봅시다.
def add(x, y):
print('Calling add')
return x + y
이제 두 번째 함수도 로깅이 추가되었습니다.
def sub(x, y):
print('Calling sub')
return x - y
관찰
관찰: 다소 반복적입니다.
코드 복제가 많은 프로그램을 작성하는 것은 종종 매우 짜증나는 일입니다. 작성하기 번거롭고 유지 관리하기 어렵습니다. 특히 작동 방식을 변경하려는 경우 (예: 다른 종류의 로깅) 더욱 그렇습니다.
로깅을 만드는 코드
아마도 로깅이 추가된 함수를 만드는 함수를 만들 수 있을 것입니다. 래퍼 (wrapper) 입니다.
def logged(func):
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
이제 사용해 봅시다.
def add(x, y):
return x + y
logged_add = logged(add)
logged에 의해 반환된 함수를 호출하면 어떻게 될까요?
logged_add(3, 4) ## You see the logging message appear
이 예제는 소위 래퍼 함수를 생성하는 과정을 보여줍니다.
래퍼는 다른 함수를 감싸서 추가적인 처리를 수행하지만, 그렇지 않으면 원래 함수와 정확히 동일한 방식으로 작동하는 함수입니다.
>>> logged_add(3, 4)
Calling add ## Extra output. Added by the wrapper
7
>>>
참고: logged() 함수는 래퍼를 생성하고 결과를 반환합니다.
데코레이터 (Decorators)
함수 주위에 래퍼를 사용하는 것은 Python 에서 매우 흔합니다. 너무 흔해서, 이를 위한 특별한 구문이 있습니다.
def add(x, y):
return x + y
add = logged(add)
## Special syntax
@logged
def add(x, y):
return x + y
특별한 구문은 위에서 보여준 것과 정확히 동일한 단계를 수행합니다. 데코레이터는 단지 새로운 구문일 뿐입니다. 함수를 *데코레이트 (decorate)*한다고 말합니다.
해설
여기에 제시된 것보다 데코레이터에는 훨씬 더 미묘한 세부 사항이 많이 있습니다. 예를 들어, 클래스에서 사용하는 경우나, 함수와 함께 여러 데코레이터를 사용하는 경우 등이 있습니다. 하지만 이전 예제는 데코레이터의 사용이 어떻게 발생하는지를 잘 보여줍니다. 일반적으로, 광범위한 함수 정의에서 반복적인 코드가 나타나는 것에 대한 대응으로 사용됩니다. 데코레이터는 해당 코드를 중앙 정의로 이동시킬 수 있습니다.
연습 문제 7.10: 시간 측정을 위한 데코레이터
함수를 정의하면, 해당 함수의 이름과 모듈은 __name__ 및 __module__ 속성에 저장됩니다. 예를 들어:
>>> def add(x,y):
return x+y
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>>
timethis.py 파일에서, 함수가 실행되는 데 걸리는 시간을 출력하는 추가적인 로직 레이어로 함수를 감싸는 데코레이터 함수 timethis(func)를 작성하십시오. 이를 위해, 다음과 같이 시간 측정 호출로 함수를 감싸야 합니다:
start = time.time()
r = func(*args,**kwargs)
end = time.time()
print('%s.%s: %f' % (func.__module__, func.__name__, end-start))
다음은 데코레이터가 작동하는 방식의 예입니다:
>>> from timethis import timethis
>>> @timethis
def countdown(n):
while n > 0:
n -= 1
>>> countdown(10000000)
__main__.countdown : 0.076562
>>>
토론: 이 @timethis 데코레이터는 모든 함수 정의 앞에 배치될 수 있습니다. 따라서 성능 튜닝을 위한 진단 도구로 사용할 수 있습니다.
요약
축하합니다! 함수 데코레이터 (Function Decorators) 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.