decorator를 알기 전 First-class 함수와 Closure 함수를 알아야 한다.

 

우선 제일 중요한 것은

파이썬에는 함수는 객체

라는 것이다.

 

 

First-class function

코드부터 보자

def info(name):
	return 'Hi ' + name

info함수는 아래와 같이 실행할 수 있다.

info('Sso')

또한 함수는 객체이기 때문에 변수에 할당할 수 있다.

func = info
print(func('Sso'))

Hi Sso

 

 

다른 코드를 봐보자

def one(num):
	return num + 1

def two(num):
	return num + 2

def three(num):
	return num + 3

위에 정의한 함수를 다른 함수의 인자로 넣을 수 있다.

def sum_num(func, num):
    print(func(num))
sum_num(one, 3)
sum_num(two, 3)
sum_num(three, 3)

4

5

6

 

sum_num함수를 저렇게 극단적으로 쓰진 않겠지만.. 어쨌든 이 또한 가능하다.

 

 

마지막으로 함수를 return할 수 있다.

def letter(text):
    msg = text
    def write_msg():
        print(msg)
    return write_msg

send = letter('HBD!')
send()

HBD!

 

letter함수 안에 write_msg 중첩함수를 정의했다.

 

위에서 함수를 변수에 할당할 수 있었기에 send 변수에 letter 함수를 할당하였고

return값이 write_msg 함수이기 때문에

send()로 write_msg 함수를 실행시킬 수 있다.

 

원래 중첩함수는 외부에서 접근할 수 없다. (write_msg()는 오류남)

하지만 함수의 반환값으로 중접함수를 사용하면 접근이 가능하다

 

본래 letter 함수를 삭제해보겠다

del letter

send()

HBD!

 

letter 함수를 삭제했는데도 HBD!가 출력됐다.

이는 letter함수를 실행할 때 넘어온 text의 값이 유지된다는 것이다.

지역 변수 값이 살아있다!

 

현재 사용하려는 함수의 외부에서 선언한 변수를 자유변수라고 한다.

 

직접 코드로 확인해보자.

print(send.__code__.co_freevars)

('msg',)

 

mgs가 저장되어져 있음을 확인할 수 있다.

실제 값을 확인해보자.

print(send.__closure__[0].cell_contents)

HBD!

 

앞서 msg(자유변수)에 HBD!를 넣어줬기 때문에 해당 값이 저장돼있음을 확인할 수 있다.

 

※ 주의 ※ 

def letter(text):
    msg = text
    def write_msg():
    	msg += '!!!!!'
        print(msg)
    return write_msg

send = letter('HBD!')
send()

위에서 자유변수를가 함수가 끝난 이후에도 살아있음을 확인했다.

하지만 write_msg함수 내에서 자유변수 msg를 변경하려고 하자 위와 같은 에러가 발생했다.

write_msg함수 내 msg가 할당되기 전에 사용했다는 에러이다.

 

write_msg함수 바깥쪽에 msg와 안쪽에 msg가 같다는 예약어가 필요하다.

바로 nonlocal 예약어이다.

def letter(text):
    msg = text
    def write_msg():
        nonlocal msg
        msg += '!!!!!'
        print(msg)
    return write_msg

send = letter('HBD!')
send()

제대로 실행된다.

Closure function

이게 바로 Closure function이다. (하나의 기법을 의미)

위에서 클로저 함수는 write_msg가 된다.

 

위키에 따르면 클로저 함수를 다음과 같이 정의한다.

클로저는 함수를 저장한 레코드(record)이며, 스코프(scope)의 인수(Factor)들은 클로저가 만들어질 때 정의(define)되며, 스코프 내의 영역이 소멸(remove)되었어도 그에 대한 접근(access)은 독립된 복사본인 클로저를 통해 이루어질 수 있다.

 

이제 Decorator를 이해할 준비가 끝났다.

 

Decorator

데코레이터 또한 하나의 기법이다.함수 앞 뒤에 기능을 추가할 수 있다!

 

그냥 코드를 봐보자

def deco_func(my_func):
    def inner_func():
        print('함수 앞 기능')
        my_func()
        print('함수 뒤 기능')
    return inner_func

inner_func은 클로저 함수이다.

 

데코레이터를 사용하지 않았을 때의 경우는 아래 코드와 같다.

def deco_func(my_func):
    def inner_func():
        print('함수 앞 기능')
        my_func()
        print('함수 뒤 기능')
    return inner_func

def star():
    print('* * * * *')

no_deco = deco_func(star)
no_deco()

print(no_deco.__code__.co_freevars)

자유변수로 my_func을 받고 있음을 확인할 수 있다.

 

 

데코레이터를 적용해보자.

@deco_func
def star():
    print('* * * * *')

star()

 

어렵지 않다..!

 

데코레이터 함수를 정의한 후

@를 사용해 데코레이터 함수명을 적어준 후

함수 앞 뒤 기능 사이에 실행할 함수를 정의하면 된다.

 

 

가변인자를 사용해서 모든 변수를 받을 수 있는 데코레이터는 아래와 같다.

(지난 글 참고(가변인자) : https://sso-y.tistory.com/11)

def decorator_all(function):
    def inner_func(*args, **kwargs):
        return '*' + function(*args, **kwargs) + '*'
    return inner_func