새소식

ETC

@데코레이터

  • -

@데코레이터란?

함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다.
함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다 .

  • 일반적으로 함수의 전처리나 후처리에 대한 필요가 있을때 사용을 한다.
  • 또한 데코레이터를 이용해, 반복을 줄이고 메소드나 함수의 책임을 확장한다.

logger_byeol 함수에 inner_func 함수의 기능을 추가해 만든 decorated_func 함수는 데코레이터를 사용할 때와 결과는 같다.
# 데코레이터 func 정의
def outer_func(func):
  def inner_func():
    print("[Log] start")
    func()
    print("[Log] end")
  return inner_func

# 데코레이터를 사용할 함수 정의
def logger_byeol():
	print("Byeol login")

decorated_func = outer_func(logger_byeol)
decorated_func()
[Log] start
Byeol login
[Log] end

 

 

위와 같은 기능을 @데코레이터로 작성한다면

# 데코레이터 func 정의
def outer_func(func):
  def inner_func():
    print("[Log] start")
    func()
    print("[Log] end")
  return inner_func


@outer_func
def logger_byeol2():
  print("Byeol login")

logger_byeol2()
[Log] start
Byeol login
[Log] end

 

 

위에서 만든 함수는 파라미터가 없었다.

그렇다면 파라미터가 있는 함수에 @데코레이터를 적용하려면 어떻게 해야할까?

 

중첩함수(Nested Function)에 동일한 파라미터를 선언한다.

# 데코레이터 func 정의
def outer_func(func):
  def inner_func(num1, num2):
    if num2 == 0:
      print("유효성 검사: 0으로는 나눌 수 없습니다.")
      return
    func(num1, num2)
  return inner_func


@outer_func
def divide(num1, num2):
  print(num1 / num2)

divide(10, 0)
유효성 검사: 0으로는 나눌 수 없습니다.

 

 

그런데 여러 함수에 적용하려고 하면 파라미터를 계속 바꿔주어야 하는데 불편하다.

파라미터와 관계없이 모든 함수에 적용하는 @데코레이터를 만들려면?

  • @데코레이터의 내부함수 파라미터를 (*args, **kwargs)로 작성한다.
*args는 가변 인자를 위한 변수이다. 즉, 함수의 인자를 몇개 받을지 모르는 경우에 사용한다. 튜플 형태로 값을 저장한다.
**kwargs가 *args와의 차이점은 딕셔너리 형태로 값을 저장하고, 파라미터 명을 같이 보낼 수 있다는 점이다.

 

 

한 함수에 여러 @데코레이터를 적용할 수 도 있다.

@decorator1의 func에 @decorator2 def test()를 인자로 받기 때문에 아래와 같이 출력이 된다.

def decorator1(func):
  def wrapper():
    print("decorator1")
    func()
  return wrapper

def decorator2(func):
  def wrapper():
    print("decorator2")
    func()
  return wrapper

@decorator1
@decorator2
def test():
  print("test")

test()
decorator1
decorator2
test

다음과 같이 응용할 수 있다.

def mark_bold(func):
  def wrapper(*args, **kwargs):
    return '<b>' + func(*args, **kwargs) + '</b>'
  return wrapper

@mark_bold
def get_text(string):
  return string

print(get_text('술 마시고 싶다.'))
<b>술 마시고 싶다.</b>

 

 

@데코레이터를 클래스의 메소드에 적용하려면? 

클래스 method의 첫 파라미터가 self이므로 이 부분을 @데코레이터 작성 시에 포함시키면 된다.

def h1_tag(func):
  def wrapper(self, *args, **kwargs):
    return f"<h1>{func(self, *args, **kwargs)}</h1>"
  return wrapper

class Champion:
  def __init__(self, name, level):
    self.name = name
    self.level = level
  
  @h1_tag
  def get_info(self):
    return self.name + " " + str(self.level)


# 데코레이터 적용 결과 확인
draven = Champion("드레이븐", 1)
print(draven.get_info())
<h1>드레이븐 1</h1>

 

 

그럼 @데코레이터 함수에 파라미터를 선언하고 싶다면?

  • 중첩 함수를 하나 더 깊게 둔다.
def decorator1(num):
  def outer_wraaper(func):
    def inner_wrapper(*args, **kwargs):
      print(f'decorator의 파라미터: {num}')
      return func(*args, **kwargs)
    return inner_wrapper
  return outer_wraaper

@decorator1(1)
def test():
  print('test')

test()
decorator의 파라미터: 1
test

 

 

728x90

'ETC' 카테고리의 다른 글

@staticmethod 데코레이터  (0) 2022.07.01
[Python] 매개 변수 & 변수 범위  (0) 2022.06.24
[Python] argparse  (0) 2022.06.22
[Python] 자료형  (0) 2022.06.21
Nested Function / First-class Function / Closure Function  (0) 2022.04.14
Contents