[코드잇] 파이썬 데코레이터(Decorator)에 파라미터 넣기

코드잇

안녕하세요, 온라인 코딩 스쿨 코드잇입니다.

저번에 '파이썬에서 데코레이터란(decorator)??'이라는 글에서 데코레이터의 사용법을 알아보았는데요.

이번에는 데코레이터에 파라미터를 넣는 방법을 알아보겠습니다.

일단 다음과 같은 함수가 있다고 합시다. 이 코드는 지금 어떤 사람의 나이를 받아서 그 사람이 술을 마셔도 되는 나이인지를 알려줍니다. 술을 마셔도 되면 '음주 가능'라고 출력하고 True를 리턴합니다.

만약 술을 마셔도 되는 나이가 아니라면 '아직 음주 가능 나이가 아닙니다!'라고 출력하고 False를 리턴하는데요.

def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다!')
  return False

그런데 한 가지 수정사항이 생겼습니다. 만약 술을 마셔도 되는 나이가 아니면 부모님께 알리기 전에 돌아가라는 경고문을 출력하기로 한 겁니다. 그리고 이 경고문은 때에 따라 계속 바뀔 수 있습니다.

바로 이럴 때 파라미터가 있는 데코레이터를 사용하면 문제를 해결할 수 있습니다. 아래와 같이 고쳐주면 됩니다.

def add_warning(warning):
 
 def decorator(func):

   def decorated_func(age):
    if func(age) == True:
     return True
    else:
     print(warning)
     return False

   return decorated_func

 return decorator



@add_warning('부모님께 알리기 전에 돌아가십시오!')
def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다.')
  return False

일단 아래에 is_drinking_age 라는 함수가 있고, 그 위에 add_warning 이라는 함수가 있습니다.

그런데 add_warning 에 지금 파라미터로 '부모님께 알리기 전에 돌아가십시오!'라는 경고문이 들어가있죠?

이게 방금 전 말한 수정사항을 반영한 건데요. 이렇게 데코레이터에 파라미터가 있을 때는 어떻게 코드를 해석해야 할까요?

바로 말씀드리자면, 위 코드에서 아래와 같은 부분은

@add_warning('부모님께 알리기 전에 돌아가십시오!')
def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다.')
  return False

아래 코드와 같은 뜻입니다.

is_drinking_age = (add_warning('부모님께 알리기 전에 돌아가십시오!'))(is_drinking_age)

그러니까 add_warning 함수가 리턴하는 함수is_drinking_age 함수를 데코레이팅해줍니다. 이 코드를 add_warning 함수의 내부와 연결지어서 설명드릴게요.

def add_warning(warning):
 
 def decorator(func):

   def decorated_func(age):
    if func(age) == True:
     return True
    else:
     print(warning)  # 수정사항을 반영하는 코드
     return False

   return decorated_func

 return decorator 

지금 add_warning 함수 안에는 decorator 라는 함수가 있고, decorator 함수 안에는 또 decorated_func 함수가 있죠?

지금 실질적으로 is_drinking_age 함수를 꾸며주는 함수는 decorator 라는 함수입니다. 헷갈리지 마시라고 일부러 이름도 decorator 적어둔 건데요. 그러니까 지금 add_warning 함수는 실제로 데코레이팅을 하는 decorator 함수에 대해 껍데기 역할을 하고 있는 겁니다.

그러니까 여기서 add_warning 함수는 바로 is_drinking_age 함수를 파라미터로 받는 것이 아니라 경고문을 파라미터로 받고 단지 진짜 데코레이팅을 하는 함수인 decorator 함수를 리턴해줍니다. 그리고 decorator 함수에서 add_warning 함수의 파라미터 warning을 사용하죠.

그러니까 방금 본 이 코드가

is_drinking_age = (add_warning('부모님께 알리기 전에 돌아가십시오!'))(is_drinking_age)

이것과 같은 뜻이라는 거죠.

is_drinking_age = decorator(is_drinking_age)

결국 decorator 함수가 원래 함수인 is_drinking_age 함수를 이전에 배운 것처럼 데코레이팅해주는 겁니다. add_warning 함수의 내부를 자세히 살펴볼까요?

def add_warning(warning):
 
 def decorator(func):

   def decorated_func(age):
    if func(age) == True:     # (1) 원래 함수를 사용해서 음주 가능 나이 여부 확인
     return True
    else:
     print(warning)  # (2) 수정사항을 반영하는 코드
     return False

   return decorated_func

 return decorator 

그럼 (1)처럼 func 함수(여기서 원래 함수인 is_drinking_age 함수)를 사용해서 음주 가능 나이인지를 확인하고, 아닌 경우에는

(2)처럼 수정사항대로 경고문을 출력해줍니다.

이때 이 경고문은 add_warning 함수의 파라미터 warning 입니다. warning에 어떤 값을 주느냐에 따라 경고문이 달라지겠죠?

데코레이터에 파라미터가 있을 때랑 없을 때가 달라서 헷갈리실 수도 있는데요.

헷갈리시면 아래 코드에서

@add_warning('부모님께 알리기 전에 돌아가십시오!')   # (1)
def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다.')
  return False

(1)부분에 add_warning('부모님께 알리기 전에 돌아가십시오!') 이 실행되어 리턴된 함수가 있다고 생각하시면 편합니다. 그럼 결국 의미적으로 아래 코드와 같은 뜻이겠죠? (1) 부분을 보세요.

@decorator   # (1)
def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다.')
  return False

그럼 한번 데코레이터가 잘 적용되었는지 맨 아래에 확인하는 코드를 2줄 추가해보겠습니다.

def add_warning(warning):
 
 def decorator(func):

   def decorated_func(age):
    if func(age) == True:
     return True
    else:
     print(warning)
     return False

   return decorated_func

 return decorator



@add_warning('부모님께 알리기 전에 돌아가십시오!')
def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다.')
  return False


is_drinking_age(17)
is_drinking_age(30)

출력해보면

아직 음주 가능 나이가 아닙니다.
부모님께 알리기 전에 돌아가십시오!
음주 가능

음주 가능 나이가 아닐 때는 부모님께 알리겠다는 경고문이 잘 출력되죠? 나중에 혹시 경고문 내용이 '술을 드시려면 나이가 될 때까지 기다리세요!' 로 바뀐다고 하면 이렇게 바꾸면 됩니다.

@add_warning('술을 드시려면 나이가 될 때까지 기다리세요!')
def is_drinking_age(age):
 if age > 19:
  print('음주 가능') 
  return True
 else:
  print('아직 음주 가능 나이가 아닙니다.')
  return False

실행해보면

아직 음주 가능 나이가 아닙니다.
술을 드시려면 나이가 될 때까지 기다리세요!
음주 가능

바뀐 경고문이 잘 출력됩니다.

데코레이터에 파라미터를 넣어서 사용하니까 좀더 유연하게 데코레이터를 사용할 수 있죠? 파라미터가 없을 때와 자칫 혼동할 수도 있지만 데코레이터에 파라미터가 있을 때는 아까 가르쳐드린 대로 골뱅이 뒤에 쓰인 부분이 그대로 실행되어 리턴된 함수 이름이 있다고 생각하시면 됩니다.

앞으로 저렇게 파라미터가 있는 데코레이터를 발견하셔도 당황하지 마세요!

파이썬으로 배우는 프로그래밍 기초가 더 궁금한다면, 코드잇에서 72시간 무료로 체험해보세요 :-)

기업문화 엿볼 때, 더팀스

로그인

/