Decorator 는 말 그대로, 다른 함수에 살짝 장식(양념?)을 하는 개념이라 생각하는게 좋겠다. 제대로 써본 적이 없어서 지금 단계에선 뭐라 말하기가 어렵지만..
책들 설명에 따르면, 주로 버그를 찾을 용도(Debugging)로 사용된다고 한다.
Introducing Python(1st Ed.) p99 에는 이렇게 설명이 나와있다.
A decorator is a function that takes one function as input and returns another function.
한 함수를 받아, 부가 기능을 추가해서 다른 함수로 변환(사실은 반환)해주는 기능. 이게 Decorator 의 역할이다.
아래 예는 Effective Python(2nd Ed.) p103 에서 가져왔다.
또, 예제에 있는 !r
은 repr
을 가리킨다.
def trace(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs) print(f'{func.__name__}({args!r}, {kwargs!r}) ' f'-> {result!r}') return result return wrapper def fibonacci(n): """Return the n-th Fibonacci number""" if n in (0, 1): return n return (fibonacci(n - 2) + fibonacci(n - 1))
fibonacci 가 원 함수고, trace 가 decorator 용이다. fibonacci 를 데코레이터로 사용하려면 다음 처럼 할당해주면 된다.
new_fibonacci = trace(fibonacci)
또, 아예 함수 정의 시에 데코레이터를 붙여버릴 수도 있다.
@trace def fibonacci(n): """Return the n-th Fibonacci number""" if n in (0, 1): return n return (fibonacci(n - 2) + fibonacci(n - 1))
@데코레이터_함수명
을 def
문 위에 써주고, 그냥 원래하는대로 함수를 정의해주면 된다.
사용은 new_fibonacci(4)
또는, @trace
를 사용했다면 그냥 fibonacci(4)
를 쓰면 된다.
***?
그런데, fibonacci 가 재귀함수를 사용해서 그런건지, 수동으로 할당했을 때와 @를 썼을 때 결과가 살짝 다르다.
# 수동 할당 후 결과: new_fibonacci(4) fibonacci((4,), {}) 3 Out[10]: 3 # @trace 후 결과 fibonacci(4) fibonacci((0,), {}) 0 fibonacci((1,), {}) 1 fibonacci((2,), {}) 1 fibonacci((1,), {}) 1 fibonacci((0,), {}) 0 fibonacci((1,), {}) 1 fibonacci((2,), {}) 1 fibonacci((3,), {}) 2 fibonacci((4,), {}) 3 Out[12]: 3
재귀함수라 좀 헷갈리지만, 아무튼 수동도, 자동도 가능하다는 사실을 기억하라.
하지만 보통은 @함수명 으로 자동방식을 사용하는 듯 하다.
한가지 문제점!
한가지는 아니고, 여러가지 문제점이 있을 수 있다고 하지만, 먼저 이렇게 간단하게 확인해볼 수 있다.
print(fibonacci.__name__) 'wrapper'
fibonacci 함수가 wrapper 로 치환(?)된 상황이기 때문에, 그 이름을 출력하면 wrapper 가 나온다. 이런 문제를 해결하려면, functools
에서 wraps
를 불러 사용해야 한다.
from functools import wraps def trace(func): @wraps(func) def wrapper(*args, **kwargs): result = func(*args, **kwargs) print(f'{func.__name__}({args!r}, {kwargs!r}) ' f'-> {result!r}') return result return wrapper
이러고 나면, fibonacci
는 그 자신의 정체성(?)을 회복하게 된다.