[PYTHON]Decorator(데코레이터) 이해하기

박민하·2022년 5월 29일
0

PYTHON

목록 보기
5/11
post-thumbnail

✅ Nested function(중첩 함수)

  다른 구문들과 마찬가지로 함수도 함수안에 중첩되어 함수 안에 함수를 선언할 수 있다. 쉽게 말해서 중첩함수란 함수 안의 함수다.

def parent_function():
    def child_function():
        print("this is a child function")

    child_function()

parent_function()
> "this is a child function"

  중첩함수(nested function) 혹은 내부 함수는 는 상위 부모 함수 안에서만 호출 가능한 로컬 함수다. 그 예로 위의 코드에서 child_function 함수는 parent_function 안에서만 호출이 가능하다.

✔ 중첩함수를 사용하는 이유?

  중첩함수를 사용하는 대표적인 이유 2가지 중 1가지는 가독성이다. 함수 안의 코드 중 반복되는 코드가 있다면 중첩함수로 선언하면 부모함수의 코드를 효과적으로 관리하고 가독성을 높일 수 있다.

  다른 1가지는 바로 Closure 이다. Closure는 폐쇄한다는 뜻을 가지고 있는데 무엇을 폐쇄한다는 것일까?

  Closure를 설명하기 전에 일단 first-class function 에 대해 먼저 알아보자.

  • 이 중첩함수를 함수 밖에서 호출할 수 있는 방법이 있다!
  • 그것은 바로 First-class function, Closure function

✅ First-class function

  First-class function(일급 함수)이란 아래의 특징을 갖는 함수를 말한다.

1. 함수를 변수에 할당할 수 있다.

# 예시
def add_num(num):
  return num + num
add = add_num    #1. add라는 변수에 addNum 함수 할당
add(5)
> 10

2. 함수를 다른 함수의 인자로 사용할 수 있다.

# 예시
def add_num(num):
  return num + num

def num_list(func, nums):
  result = list()
  for num in nums:
    result.append(func(num))
  return result 
lst = [1,2,3,4,5]
num_list(add_num, lst)    #2. addNum 함수를 다른 함수의 인자로 사용
>  [2, 4, 6, 8, 10]

3. 함수가 함수의 반환 값이 될 수 있다.

def say_hi(msg):
  def message():    #3. 함수 sayHi 안에 함수 message를 넣어 message값 반환
    print (f"안녕하세요 {msg}님!")
  return message
위코드 = say_hi("위코드")
print(위코드)    #함수 정보
# <function sayHi.<locals>.msgs at 0x000001AC3FD2B760>
위코드()
# 안녕하세요 위코드님!

✅ Closure function

  함수와 해당 함수가 가지고 있는 데이터를 함께 복사, 저장해서 별도 함수로 활용하는 기법이다(First-class 함수와 동일하다). 부모 함수(외부 함수)가 소멸되더라도 부모 함수 안에 있는 로컬 변수 값과 중첩함수를 사용할 수 있다.

  일반적으로 제공해야할 기능(method)이 적은 경우 closure를 사용하고, 그 외에는 class를 사용하여 구현한다.

  아래는 Closure 의 사용 방법이다.

1.  부모 함수의 변수(또는 정보)를 중첩 함수 내에서 사용한다.
2.  부모 함수는 return 값으로 중첩 함수를 리턴한다.
3.  부모 함수에서 return 했으므로 부모 함수의 변수는 직접적인 접근이 불가능 하지만, 부모 함수가 리턴한 중첩 함수를 통해서 사용될 수 있다.

✔ 언제 사용할까?

  어떠한 정보를 기반으로 연산을 실행하고 싶지만, 기반이 되는 정보는 접근을 제한하여 노출이 되거나 수정이 되지 못하게 하고 싶을때 사용한다. 주로 factory 패턴을 구현할때 사용되는데, 주로 함수나 오브젝트를 생성해내는데 사용된다.

  Factory에서 뭔가를 생성해 내기 위해서는 설정값이 필요할것이다. 그 설정값을 노출하지 않아서 수정이 불가능하게 하면서 해당 설정값을 기반으로한 연산을 할 수 있는 함수를 만들때 closure를 사용할 수 있다.

  예를 들어 숫자 number 의 승 power을 구하는 함수를 만든다고 해보자.

def calculate_power(number, power):
    return number ** power

calculate_power(2, 7)
> 128

  이를 closure 함수로 표현을 하면

def generate_power(base_number):
    def nth_power(power):
        return base_number ** power
    return nth_power

calculate_power_of_two = generate_power(2) #Closure 생성. 2의 승을 구하는 함수.
calculate_power_of_two(7)
> 128
calculate_power_of_two(10)
> 1024

calculate_power_of_seven = generate_power(7) #Closure 생성. 7의 승을 구하는 함수
calculate_power_of_seven(3)
> 343
calculate_power_of_seven(5)
> 16907

  또 다른 특징으로는 외부 함수가 소멸되더라도 내부 함수 사용 가능하다.

del generate_power
calculate_power_of_two(7)
> 128

✅ Decorator(데코레이터)

  드디어 decorator까지 왔다. Decorator란 함수 앞뒤에 기능을 추가해서 손쉽게 함수를 활용할 수 있는 기법이다. Closure function을 활용하여 여러 함수에 동일한 기능을 @데코레이터 하나로 간편하게 추가할 수 있다. 매개변수와 관계 없이 모든 함수에 적용 가능한 데코레이터를 만들고 싶으면 *args, **kwargs 를 사용하면 된다.

def mark_bold(function):    #글자를 굵게 하는 함수
  def wrapper(*args, **kwargs):
      return (f'<b>{function(*args, **kwargs)}</b>')
  return wrapper

def mark_italic(function):    #글자를 기울게 하는 함수
  def wrapper(*args, **kwargs):
      return (f'<i>{function(*args, **kwargs)}</i>')
  return wrapper

@mark_bold
@mark_italic
def say_hello(hello):
  return hello
print(say_hello("안녕하세요!"))
# <b><i>안녕하세요!</i></b>
profile
backend developer 🐌

0개의 댓글