Data_Analysis_Track_33/Python

Python_09-2(Decorator)

lsc99 2023. 8. 28. 18:38

Decorator(장식자)

지역함수(Local Function) : 함수 안에 정의한 함수(중첩 함수라고도 한다.)
- 지역함수가 선언된 함수를 outer function 지역함수는 inner function 이라고 한다.
- inner function은 outer function의 지역변수를 자유롭게 사용할 수 있다.
- 기본적으로 inner function은 outer function 안에서만 호출할 수 있다.
- 단 outer function이 정의된 inner function을 return value로 반환하면 밖에서도 호출 할 수 있다.

 

def 라(a): # outer function
    
    def 마(): # inner function
        
        print(a + 10)
        
    마()
    마()
    마()
    마()
    b = 10
    return 마

 

라(10) # 라 함수(outer function)에 parameter로 10 입력, 마 함수(inner function)에서 처리 가능하다

 

b = 라(1)
print(b)

 

b() # return 마로 반환했기에 11이 결과로 출력

# 변수 - global(전역) 변수, local(지역) 변수
# 변수의 사용범위 (scope) = 그 변수가 선언된 영역(block) 내에서 호출 가능. 
#                                                         -> 하위 영역(block에서도 호출, 사용가능)
# 함수에서 global 변수의 값을 변경할 경우 : global 변수명
# inner 함수에서 outer 함수의 local 변수를 변경 : nonlocal 변수명
# 하위블록에서 상위블록 변수의 값을 변경할 수는 있지만 하지 않는 것이 좋다.


global_var = "Global (전역)변수"
print(1, global_var)
def outer():
    global global_var # global 변수 선언
    
    local_var = "Outer 함수의 local (지역)변수"
    print(2, global_var)
    print(3, local_var)
    
    # 하위블록에서 global 변수값을 변경하려면 global 변수 선언한 뒤에 변경하면 된다.
    global_var = "Outer에서 변경한 global_var의 값"
    
    def inner():
        # Outer 함수의 지역변수를 변경할 경우 - nonlocal 변수명 으로 선언을 먼저해야 한다.
        nonlocal local_var # nonlocal 변수명 선언
        
        inner_local_var = "inner 함수의 local (지역)변수"
        print(4, global_var)
        print(5, local_var)
        local_var = "inner에서 변경한 local_var 의 값"
        print('5-2', local_var)
        print(6, inner_local_var)
        
    inner()
        
outer()
# print(local_var) # 상위 블록에서는 하위블록의 local 변수는 호출되지 않는다.


Closure (클로저)
- 지역함수(Inner function)를 정의한 Outer function이 종료되어도 지역함수가 종료될 때까지 outer function의 지역변수들은 메모리에 계속 유지 되어 inner function에서 사용할 수 있다.
- 파이썬 실행환경에서 inner function이 종료될때 까지 outer function의 지역변수들(parameter포함)을 사용할 수 있도록 저장하는 공간이 closure이다.

결론 : inner 함수에서 outer 함수의 변수를 사용하는데 return됨으로서 outer에 있는 지역변수도 사용할 수 있게 만든다.

어렵게 생각하지말고 이해만 하도록 하자.

 

def outer():
    outer_var = 10 # outer 함수의 지역변수
    
    def inner():
        print(outer_var) # inner 함수에서 outer 함수의 변수를 호출
    
    return inner  # inner 함수를 반환

 

f = outer() # f = inner 함수
f()


Decorator (장식자) : 기존의 함수를 수정하지 않고 그 함수 전/후에 실행되는 구문을 추가할 수 있도록 하는 함수를 말한다.
- 기존 함수코드를 수정하지 않고 새로운 기능의 추가를 쉽게 해준다.
- 추가기능을 다수의 함수에 적용할 수 있다.
- 함수의 전/후처리 하는 구문을 필요하면 붙이고 필요 없으면 쉽게 제거할 수 있다

 

def a():
    print("안녕하세요.")
    
def b():
    print("Hello!")

 

print("-"*50)
a()
print("-"*50)

print("-"*50)
b()
print("-"*50) # ->  호출하다보니 계속 함수 호출 전,후에 코드를 작성해야하는 것이 귀찮다.

def a():
    print("-"*50)
    print("안녕하세요.")
    print("-"*50)
    
def b():
    print("-"*50)
    print("Hello!")
    print("-"*50)

 

a()
a()
b()
a()
# 그리하여 함수 내부에 변경을 주어 호출하다 보니 변경점이 생기면 함수를 바꿔야 하는 일이 발생한다

# 그래서 함수를 수정하지 않고 전처리와 후처리에 기능을 추가하고 다양한 방식을 적용하는 것을 도와주는것이 Decorator이다.
def a():
    print("안녕하세요.")
    
def b():
    print("Hello!")

 

def equal_deco(func): # 파라미터 -> 함수를 받는다. -> original 기능을 실행할 함수
    
    # inner함수 : original 함수 호출을 처리하는 함수. 함수 호출 전후로 해야할 것이 있으면 그 처리를 한다.
    def wrapper():
        print("="*30) # 전처리
        func()        # 원래 함수의 작업
        print("="*50) # 후처리
    return wrapper

 

# a()
a_f = equal_deco(a)
a_f() # 실행된(호출한) 함수 : wrapper()

 

a() # 이런식으로 전,후처리를 자유롭게 처리할 수 있다.

def sharp_deco(func):
    
    def wrapper():
        print("#"*30)
        func()
        print("#"*30)
        
    return wrapper

 

# 데코레이터 함수에 오리지날 함수를 전달해서 호출 -> wrapper 함수를 반환값으로 받는다. -> wrapper 함수를 호출
a_f2 = sharp_deco(a)
a_f2()

 

b_f = equal_deco(b)
b_f()


Decorator 구현 및 사용
구현
1. 전/후처리 기능을 추가할 함수를 parameter로 받는다.
2. 그 함수 호출 전후로 추가할 기능을 작성한 지역함수를 정의한다.
3. 2번의 함수를 반환한다.

호출 : @decorator이름 적용하고자하는 함수 선언전에 기술한다.

 

# decorator를 original 함수 정의시 추가하라고 선언
@equal_deco # @함수명 -> decorator 기능 한다.
def a2():
    print("안녕하세요")

 

a2()

 

@sharp_deco
def a3():
    print("안녕하세요")

 

a3()

def sharp_deco2(func):
    # original 함수가 파라미터를 받을 경우 그것을 wrapper() 함수에 설정한다.
    def wrapper(param):
        print("#"*50)
        result = func(param) # original 함수 호출
        print("#"*50)
        if len(result) < 7: # 조건 달기(decorator에서 exception 발생시킬 수 있다.)
            raise Exception("결과값이 모자랍니다.")
            # result = "   " + result
        return result
    return wrapper

 

@sharp_deco2
def greet(name):
    print(f"{name}님 환영합니다.")
    return f"인사말 {name}"

 

# sharp_deco2.wrapper를 호출
v = greet("홍") # return되는 "인사말 {name}"이 7글자보다 작다는 조건에 만족되지 못하면 Exception 발생
print(v)