본문 바로가기

Hacks

클로저

https://shoark7.github.io/programming/python/closure-in-python

 

Python의 Closure에 대해 알아보자

Python에서 유용한 Closure에 대해 살펴봅니다.

shoark7.github.io

위의 블로그에서 발견할 수 있었던 클로저를 사용한 코드이다.

def in_cache(func):
    cache = {}
    def wrapper(n):
        print(cache) ## !!!!
        if n in cache:
            return cache[n]
        else:
            cache[n] = func(n)
            return cache[n]
    return wrapper


def factorial(n):
    ret = 1
    for i in range(1, n+1):
        ret *= i
    return ret

factorial = in_cache(factorial)
factorial(4)
factorial(7)
factorial(10)

정말 신기하게도 cache값이 전역변수처럼 출력이 된다. 값이 누적이 된다.

근데 아직 이해를 못하겠다.

정확하게 클로저가 뭐다!

이렇게 설명을 못하겠다.

 


클로저가 무슨 기능을 하고 어떻게 돌아가는 건지 조금은 이해했다.

그래서 여기에 기록하려고 한다!

 

클로저를 왜 써?

1. 가장 이해가 잘 됐던 설명은 협업 부분이었다. 많은 사람들이 내가 작성한 코드를 터치할 것이다. 같은 공간에서 코드를 짜게될테니까 말이다.

그럴 때 내가 저장해둔 데이터를 안전하게 지켜줄 수 있는 역할을 한다.

클로저 안에 만들어놓은 기능을 통해서만 데이터에 변화를 줄 수 있게 되기 때문이다!

 

2. 메모이제이션에서 좋은 효율을 보인다.

 

클로저가 뭐야?

부 함수가 외부 함수의 지역 변수를 참조함으로써 외부 함수가 소멸된 이후에도 소속 지역 변수가 사라지지 않는 이슈를 클로저(Closure)라고 한다.

원래 함수는 return을 하면 메모리에서 소멸되지만 그 함수의 환경을 변수에 가둬두는 것이다!그래서 함수가 죽더라도 함수의 기능을 사용할 수 있게 된다!

 

클로저를 만들기 위한 3가지 조건

1. 클로저는 항상 외부 함수에 의해 감싸져 있어야 한다.2. 외부 함수에 있는 상태값을 참조해야 한다. 즉, nonlocal위치의 상태값를 참조해야 한다.3. 외부 함수는 클로저를 반환해야 한다.

 

코드를 보면서 위에서 설명했던 3가지를 이해해보자

무슨 코드를 적을 건지 먼저 설명하고자 한다.

바라는 클로저의 기능

1. 영화 제목을 출력시키고자 한다. (getter)

2. 바꾸고 싶다면 출력시키고자 하는 영화의 제목을 바꿀 수 있어야 한다. (setter)

 

처음에는 아래처럼 구현했다.

def title_factory(title):
    def get_title():                # getter
        return title
    def set_title(_title):          # setter
        title = _title
    return get_title, set_title

ghost = title_factory("Ghost")
print(ghost[0]())
ghost[1]("Changed")                 # setter가 작동하지 않음
print(ghost[0]())

위처럼 코드를 짜니 get_title()은 잘 작동하는데 set_title은 작동하지가 않았다.

안 그래도 set_title의 title 부분이 참조된 적이 없다는 메시지가 뜨기도 했다.

 

바로 뭐가 문제였는지 보자.

def title_factory(title):
    def get_title():                # getter
        return title
    def set_title(_title):          # setter
        nonlocal title
        title = _title
    return get_title, set_title

ghost = title_factory("Ghost")
print(ghost[0]())
ghost[1]("Changed")             
print(ghost[0]())

# Ghost
# Changed

코드의 흐름을 살펴보자.

1. get_title()은 title_factory의 파라미터인 title을 참조한다.

 

2. set_title()은 _title을 받아서 title에 저장한 다음, title_factory의 파라미터인 title에 변화를 준다.

 

중요한 건 title_factory 함수는 getter와 setter를 반환하고 메모리적으로 죽는다.

하지만 우리는 클로저를 통해 여전히 영화 제목이 뭔지 제목 데이터를 얻을 수 있고 심지어 제목을 바꿔줄 수도 있다.

그리고 아까 이야기했던 것처럼 데이터를 안전하게 저장해줄 수 있다.

왜?

 

누군가 무심코 똑같은 변수 이름을 선언해서 어떤 값을 대입해서 정보를 바꾸고자 한다면, 에러가 날 것이다.

즉, 누군가 쉽게 데이터 값을 변경할 수가 없게 된다.

클로저를 통해 값이 남아있게 되는 것이다!

이것이 variable capture이다!

 


맨 위에 걸어둔 블로그에서 도움을 받아 작성하기도 했고

https://hongl.tistory.com/248 이 블로그에서도 도움을 받았다.

 

가장 많이 도움을 받은 곳은 https://www.youtube.com/watch?v=J0Qb2wjm-ik 생활코딩 채널의 클로저 설명이다!

이곳을 통해 깨달음을 얻을 수 있었다!