[Python] 클로저(closure), 데코레이터

2 분 소요

함수를 만들어 봔환하는 함수, 클로저(Closure)

>>> def maker(m):
        def inner(n):
            return m*n
        return inner

>>> f = maker(2)
>>> f(7)  # 실제 변수 m을 참조하게 되는 순간! maker함수의 밖이다.
14

함수를 반환하는 함수의 예이다. 파이썬의 함수는 객체이므로 다른 함수를 호출할 때 인자로도 전달이 가능하다.

maker함수의 m은 maker함수를 벗어나면 사라진다. 그런데 inner 함수에는 m*n 즉 m이 존재한다. m의 값을 참조하는 위치는 maker함수의 안이 아니라 다음 문장이 실행되는 maker함수 밖이다.

위 코드에서 정의한 inner 함수가 변수 m의 값을 어딘가에 살짝 저장해 놓고 쓴다.

이렇게 안쪽에 위치한 네스트드 함수가 자신이 필요한 변수의 값을 어딘가에 저장해 놓고 쓰는 테크닉을 클로저라 한다.

>>> def maker(m):
        def inner(n):
            return m*n
        return inner

>>> f = maker(2)
>>> f.__closure__[0].cell_contents # 변수 m의 값을 저장해 놓은 위치
2

클로저는 네스티드 함수가 자신이 필요한 변수값을 어딘가에 저장해 놓는 기술이다. 그 위치는 위 코드로 확인이 가능하다. f.closure[0].cell_contents 의미는 변수의 인덱스 0의 위치에 저장된 객쳉듸 변수인 cell_contents이다.



데코레이터

데코레이터(Decorator)는 꾸며주는 역할을 하는 함수 또는 클래스이다.

>>> def smile():
        print('^_^')

>>> def confused():
        print('@_@');

>>> def deco(func):  # 데코레이터 함수, 줄여서 데코레이터
        def df():
            print('emotion!')  # 추가된 기능
            func()             # 원래 기능
            print('emotion!')  # 추가된 기능
        return df   # 보강된 기능의 함수를 반환

>>> smile = deco(smile)  # 함수를 데코레이터에 통과시킴
>>> smile()
emotion!
^_^
emotion!
>>> confused = deco(confused)
>>> confused()
emotion!
@_@
emotion!



전달 인자가 있는 함수 기반의 데코레이터

>>> def adder2(n1, n2):
        return n1+n2

>>> def adder3(n1, n2, n3):
        return n1+n2+n3

>>> def adder_deco(func):
        def ad(*args):
            print(*args, sep='+', end=' ')
            print("= {0}".format(func(*args)))
        return ad

>>> adder2 = adder_deco(adder2)
>>> adder2(3,4)
3 + 4 = 7

위 코드에서 데코레이터를 정의하는데 있어서 튜플의 패킹과 언패킹이 사용되었다. 전달인자의 수에 상관없이 데코레이터를 정의했다.

@ 기반

def adder_deco(func):       # 데코레이터 함수
    def ad(*args):
        print(*args, sep = ' + ', end = ' ')
        print("= {0}".format(func(*args)))
    return ad


@adder_deco  # 아래 함수를 데코레이터 adder_deco에 통과시켜라
def adder2(n1, n2):       # 전달 인자가 두 개인 함수
    return n1 + n2

@adder_deco  # 아래 함수를 데코레이터 adder_deco에 통과시켜라
def adder3(n1, n2, n3):       # 전달 인자가 세 개인 함수
    return n1 + n2 + n3


def main():
    adder2(3, 4)
    adder3(3, 5, 7)

main()
3 + 4 = 7
3 + 5 + 7 = 15

앞에서 보인 예제들은 함수정의와 데코레이터 통과 과정이 분리되어 있었다. 위 코드는 함수정의와 데코레이터 통과과정을 합쳤다.



데코레이터 함수 두번 이상 통과하기

def deco1(func):       # 데코레이터 1
    def inner():
        print('deco1')
        func()
    return inner

def deco2(func):       # 데코레이터 2
    def inner():
        print('deco2')
        func()
    return inner

@deco1
@deco2
def simple():
    print('simple')


def main():
    simple()

main()
deco1
deco2
simple

함수를 정의하면서 @기반으로 데코레이터를 두번이상 통과시킬 수 있다.

위 코드에서 simple = deco1(deco2(simple))를 하면 두번 통과시키는 것이다. 하지만 이것을 @기반으로 위 코드처럼 두번이상 통과가 가능하다.