개발기록장

[Python] 파이썬 특수문법 - 중첩 함수와 데코레이터 본문

TIL/Python

[Python] 파이썬 특수문법 - 중첩 함수와 데코레이터

yangahh 2021. 1. 19. 03:48

 

 

1. 중첩 함수(Nested Function)

- 중첩 함수란?

  • 중첩 함수란 함수 안에 정의된 또 다른 말한다. 내부 함수라고도 하고, 중첩 함수를 감싸고 있는 함수를 외부 함수라고도 한다.

  • 중첩 함수는 해당 함수가 정의된 함수 내에서만 호출할 수 있다.

  • 예시
# 외부 함수 선언
def outer_function():

	# 중첩 함수( = 내부 함수) 선언
    def inner_function():
        print("this is a inner function")

    inner_function()

outer_function()


# 결과
> this is a inner function

 

- 중첩 함수를 사용하는 이유

중첩 함수를 사용하는 이유에는 크게 2가지가 있다.

  1. 가독성을 높이기 위해 - 반복되는 코드 블럭을 함수로 정의해서 효과적으로 코드를 관리하고 가독성을 높일 수 있다.

  2. 클로저(Closure)를 사용하기 위해 -  클로저 개념은 아래에서 알아보자.

 

- 클로저 (Closure) 

클로저란 함수와 해당 함수가 가지고 있는 데이터(지역 변수, 코드 등)를 함께 복사, 저장하고 있다가 함수를 호출할 때 다시 꺼내서 사용하는 함수를 클로저(closure)라고 한다.

예시)

def generate_power(base_number):

	# 중첩 함수에서 외부 함수의 변수에 접근 가능
    def nth_power(power):
        return base_number ** power

    # 중첩 함수 이름을 리턴
    return nth_power


# generate_power(2)함수를 calculate_power_of_two라는 변수에 저장
calculate_power_of_two = generate_power(2)  


# 저장해둔 함수를 호출. 즉, Closure호출. Closure호출시 내부 함수가 실행됨
print(calculate_power_of_two(7))            


# 결과 (2**7의 결과)
> 128


## 참고 ##
print(calculate_power_of_two)
# 결과
<function generate_power.<locals>.nth_power at 0x7f8fd01ca4c0>

위 예제에서 calculate_power_of_two에 저장된 함수가 바로 closure이다.

calculate_power_of_two = generate_power(2) 에서 generate_power 함수는 호출이 종료되었고

calculate_power_of_two()은 결국 nth_power 함수를 호출한 것이다.

즉, closure를 이용하면 부모 함수(=외부 함수)가 리턴한 중첩 함수를 통해서 외부 함수의 변수에 접근할 수 있다.

 

따라서 부모 함수의 변수를 외부로부터 직접적인 접근은 막으면서도 중첩 함수를 통해서 격리된 부모 함수의 변수를 이용한 연산은 가능하게 해준다.

 

 

이러한 기능이 있는 closure은 언제 사용할까?

어떠한 정보를 기반으로 연산을 실행하고 싶지만 기반이 되는 정보는 접근을 제한하여 노출이 되거나 수정이 되지 못하게 하고 싶을때 사용한다. 

** 일반적으로 제공해야할 기능(method)이 적은 경우, closure를 사용하기도 하고

    제공해야할 기능(method)가 많은 경우는 class를 사용하여 구현하기도 한다.

 

 

 

 

 

2. Decorator

- 데코레이터(Decorator)란?

  • 함수 앞뒤에 기능을 추가해서 손쉽게 함수를 활용할 수 있는 기법을 말한다.

  • 즉, 함수(메서드)를 장식한다고 해서 이런 이름(decorate = 장식하다)이 붙었다.

  • 데코레이터는 Closuer 함수를 활용한 기법이다.

  • 중첩 함수(nested function)을 리턴하는 함수만 decorator 함수로 사용될 수 있다.

  • 여러개의 함수가 연속적으로 호출이 자동으로 되게 해줘야 하기 때문이다.

 

 

- 사용법

예제 : 유료 회원에 한해서 대박 주식 정보를 알려주는 함수인 jackpot_stock_info가 있다고 하자.

          jackpot_stock_info 함수 실행전에 해당 유저가 유료 유저인지 꼭 확인해야 한다.

         이를 데코레이터로 구현해보자.

# 데코레이터 함수 작성
def is_paid_user(func): # func는 이 함수 안에 넣을 함수 이름을 말함
    user_paid = True    # 테스트를 위해 true로만 설정
	
    # 실제 실행하려고 한 함수를 중첩 함수를 통해 실행시킨다.
    def wrapper():      # 호출할 함수를 감싸는 함수 
        if user_paid:
            return func()
        else:
            return
            
    return wrapper      # closure 함수로 만듦



# 데코레이터 함수 적용
@is_paid_user
def jackpot_stock_info():
    return "buy now!"
    

# 데코레이터가 적용된 함수 실행
print(jackpot_stock_info())



# 결과
> buy now!

 

 

위 예제를 데코레이터를 사용하지 않고 코드를 짠다면 아래와 같다.

def is_paid_user(func):
    user_paid = True  

    def wrapper():
        if user_paid:
            return func()
        else:
            return
    return wrapper


def jackpot_stock_info():
    return "buy now!"


# 먼저 데코레이터 함수를 실행시키는 구문(=이 함수를 실행시킬 세팅이 되었다고 보면됨)이 어떤 변수에 할당되고, 
# 데코레이터 함수를 호출하는 구문에서는 인자값으로 실제 호출하고자 하는 jackpot_stock_info 함수가 전달됨
check_user_first = is_paid_user(jackpot_stock_info)


# 데코레이터 함수(->클로저 개념 이용)가 실행시키는 변수를 함수처럼 실행하면, 
# 데코레이터 함수의 내부 함수가 호출되는데 내부 함수는 jackpot_stock_info 함수를 리턴하므로 이때 jackpot_stock_info 함수가 실행된다.
print(check_user_first())

 

 

- 왜 사용할까?

여러 함수에 동일한 기능을 @decorator_function 하나로 간편하게 추가할 수 있기 때문이다.

예를 들면, 파라미터가 있는 함수에 파라미터 유효성 검사가 필요할 경우, 데코레이터를 사용하면 매우 편리하다.

데코레이터를 사용하지 않는다면 파라미터가 있는 함수가 있을 때마다 유효성 검사 코드를 넣어야 하고,

만약 그 유효성 검사 코드에 수정이 필요하다면 관련 함수를 모두 수정해야 하는 불편함이 있기 때문이다.

 

 

 

 

 

 

참고)
www.fun-coding.org/PL&OOP4-3.html