함수를 수정하지 않은 상태에서 추가 기능을 구현할 때 사용한다.
def hello():
print("hello 함수 시작")
print("hello")
print("hello 함수 끝")
def world():
print("world 함수 시작")
print("world")
print("world 함수 끝")
hello()
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝
world()
# 결과값
# world 함수 시작
# world
# world 함수 끝
위 코드와 같이 "(함수명) 함수시작" 이라는 문자열과 "(함수명) 함수 끝" 이라는 문자열을 출력하고 싶을 때 일일히 코드를 작성하는 것보다 데코레이터를 사용하면 더욱 쉽게 사용할 수 있고 코드의 재사용이 가능하다는 장점이 있다.
def trace(func): # 호출할 함수를 매개변수로 받음
def wrapper(): # 호출할 함수를 감싸는 함수
print(func.__name__, "함수 시작") # __name__으로 함수 이름 출력
func() # 매개변수로 받은 함수를 호출
print(func.__name__, "함수 끝") # __name__으로 함수 이름 출력
return wrapper # wrapper 함수 반환
def hello():
print("hello")
def world():
print("world")
trace_hello = trace(hello) # 데코레이터에 호출할 함수를 넣음
trace_hello() # 반환된 함수를 호출
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝
trace_world = trace(world) # 데코레이터에 호출할 함수를 넣음
trace_world() # 반환된 함수를 호출
# 결과값
# world 함수 시작
# world
# world 함수 끝
@로 데코레이터 사용하기
def trace(func):
def wrapper():
print(func.__name__, "함수 시작")
func()
print(func.__name__, "함수 끝")
return wrapper
@trace # @ 데코레이터
def hello():
print("hello")
@trace
def world():
print("world")
# 함수를 그대로 사용
hello()
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝
world()
# 결과값
# world 함수 시작
# world
# world 함수 끝
함수 명은 바꿔도 상관은 없다.
데코레이터 여러개 지정하기
def decorator1(func):
def wrapper():
print("decorator1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("decorator2")
func()
return wrapper
@decorator1
@decorator2
def hello():
print("hello")
hello()
# 결과값
# decorator1
# decorator2
# hello
# 데코레이터 2개를 사용했는데 hello가 한개만 찍힌다
# 과정순서가 decorator1(decorator2(hello)) 순으로 진행되는것 같다
매개변수와 반환값을 처리하는 데코레이터
def trace(func):
def wrapper(a, b):
res = func(a, b) # fucn에 매개변수 a 와 b를 넣어서 호출하고 반환값을 변수에 저장
print(f"{func.__name__}(a = {a}, b = {b}) -> {res}")
return res # func의 반환값을 반환
return wrapper
@trace
def add(a, b):
return a + b
print(add(10, 20))
# 결과값
# add(a = 10, b = 20) -> 30
# 30
wrapper의 파라미터 a, b는 파이썬에서 자동으로 add(a, b)로 들어간 파라미터로 값을 넣어준다.
trace 쪽 func 에는 함수를 전달, wrapper 에는 인수를 전달한다.
그 이후 wrapper 에서 func 파라미터로 전달받은 wrapper 파라미터의 값을 넣어준다.
가변 인수 함수 데코레이터
def trace(func):
def wrapper(*args, **kwargs): # 가변 인수 함수로 만듦
res = func(*args, **kwargs)
print(f"{func.__name__}(args = {args}, kwargs={kwargs}) -> {res}")
return res
return wrapper
@trace
def get_max(*args):
return max(args)
@trace
def get_min(**kwargs):
return min(kwargs.values())
print(get_max(10, 20, 30))
# 결과값
# get_max(args = (10, 20, 30), kwargs={}) -> 30
# 30
print(get_min(x = 10, y = 20, z = 30))
# 결과값
# get_min(args = (), kwargs={'x': 10, 'y': 20, 'z': 30}) -> 10
# 10
위치인수와 키워드 인수를 모두 처리할 수 있기 때문에 일반적인 함수에서도 사용이 가능하다.
def trace(func):
def wrapper(*args, **kwargs): # 가변 인수 함수로 만듦
res = func(*args, **kwargs)
print(f"{func.__name__}(args = {args}, kwargs={kwargs}) -> {res}")
return res
return wrapper
@trace
def add(a, b):
return a + b
# 결과값
# add(args = (10, 20), kwargs={}) -> 30
# 30
클래스 메서드에 데코레이터 사용하기
클레스에서 데코레이터를 사용할 때는 self 에 주의해야 한다.
def trace(func):
def wrapper(self, a, b): # 첫번째 매개변수는 self로 지정
r = func(self, a, b) # self와 매개변수르 그대로 넣어줌
print(f"{func.__name__}(a = {a}, b = {b}) -> {r}")
return r
return wrapper
class Calc:
@trace
def add(self, a, b):
return a + b
c = Calc()
print(c.add(10, 20))
# 결과값
# add(a = 10, b = 20) -> 30
# 30
클래스로 데코레이터 만들기
인스턴스를 함수처럼 호출하게 해주는 __call__ 메서드를 구현해야 한다.
class Trace:
def __init__(self, func): # 호출할 함수를 인스턴스의 초기값으로 받음
self.func = func # 호출할 함수를 인스턴스 변수 func에 저장
def __call__(self):
print(self.func.__name__, "함수 시작")
slef.func()
print(self.func.__name__, "함수 끝")
@Trace
def hello():
print("hello")
hello()
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝
@를 지정하지 않고 데코레이터의 반환값을 호출하는 방식으로도 사용이 가능하다.
class Trace:
def __init__(self, func): # 호출할 함수를 인스턴스의 초기값으로 받음
self.func = func # 호출할 함수를 인스턴스 변수 func에 저장
def __call__(self):
print(self.func.__name__, "함수 시작")
self.func()
print(self.func.__name__, "함수 끝")
def hello():
print("hello")
trace_hello = Trace(hello) # 데코레이터에 호출할 함수를 넣어서 인스턴스 생성
trace_hello() # 인스턴스를 호출, __call__ 메서드가 호출됨
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝
클래스로 매개변수와 반환값을 처리하는 데코레이터
class Trace:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs): # 호출할 함수의 매개변수를 처리
# self.func에 매개변수에 넣어서 호출하고 반환값을 변수에 저장
r = self.func(*args, **kwargs)
print(f"{self.func.__name__}(args = {args}, kwargs = {kwargs}) -> {r}")
return r
@Trace
def add(a, b):
return a + b
print(add(10, 20))
# 결과값
# add(args = (10, 20), kwargs = {}) -> 30
# 30
print(add(a = 10, b = 20))
# 결과값
# add(args = (), kwargs = {'a': 10, 'b': 20}) -> 30
# 30
클래스로 매개변수가 있는 데코레이터 만들기
class IsMultiple:
def __init__(self, x): # 데코레이터가 사용할 매개변수를 초기값으로 받음
self.x = x # 매개변수를 속성 x 에 저장
def __call__(self, func): # 호출할 함수를 매개변수로 받음
def wrapper(a, b):
r = func(a, b)
if r % self.x == 0: # func의 반환값이 self.x의 배수인지 확인
print(f"{func.__name__}의 반환값은 {self.x}의 배수 입니다.")
else:
print(f"{func.__name__}의 반환값은 {self.x}의 배수가 아닙니다.")
return f
return wrapper
@IsMultiple(3) # @데코레이터(인수)
def add(a, b):
return a + b
print(add(10, 20))
# 결과값
# add의 반환값은 3의 배수 입니다.
# 30
print(add(2, 5))
# 결과값
# add의 반환값은 3의 배수가 아닙니다.
# 7