이 글의 작성자는 C/C++ 프로그래밍을 하던 사람이다. 이 글은 Python을 복습하며 작성하는 글이니, 부족한 부분이 있으면 얼마든지 피드백을 주시기 바란다.

 

내부 함수도 리턴할 수 있다

저번에 우리는 내부 함수라는 것을 알아본 적이 있다. 그리고 내부 함수도 객체다. 그렇다면 내부 함수도 리턴할 수 있을까?

def hello():
    def hi():
        print('hi')
    return hi


func = hello()
func()

이 코드를 실행하면 hi가 출력되는 것을 확인할 수 있을 것이다. 내부 함수 코드 그대로 잘 실행되는 것을 확인할 수 있다.

 

클로저(closure)

그러면 이번엔 함수를 조금 수정해보자.

def hello(name):
    def hi():
        print('hi ' + name)
    return hi


func = hello('jack')
func()

무엇이 수정되었는가? hello 함수에 이름을 받는 매개변수가 추가되었고, 내부 함수인 hi는 hello가 받아들인 인자를 같이 출력하게 되었다. 그러면 위의 코드의 실행 결과는 어떻게 될까? hi jack이 출력되는 것을 확인할 수 있다.

그러면 이건 어떨까? 동일한 hello 함수에 각각 다른 인자를 전달하여 받아온 함수 객체는 어떤 결과를 보여줄까?

func = hello('jack')
func2 = hello('mike')
func3 = hello('ann')
func()
func2()
func3()

위의 코드를 실행하면 jack, mike, ann이 차례대로 출력되는 것을 확인할 수 있을 것이다. 각자 전달받은 인자 그대로 출력하고 있다는 사실을 알 수 있다.

위의 내용들은 무엇을 의미하는 것은, 각각의 함수 객체들은 자신이 받아들였던 인자를 기억하고 있다는 것을 의미한다. 무려 함수 호출이 끝나고, 동일한 함수를 다른 인자를 이용하여 또 다시 전달을 했음에도 불구하고 말이다!

이러한 형태의 함수 객체를 클로저(closure)라고 부른다. 함수의 호출이 끝나서 더 이상은 사용할 일이 없는 매개변수 name의 내용을 클로저는 계속해서 가지고 있는 것이다.

이것은 굳이 매개변수로 들어온 것이 아니라, 내부 함수를 포함하고 있는 함수의 지역 네임스페이스에 속하는 변수가 되어도 동일하게 적용될 수 있다. 요는 지역 네임스페이스를 벗어나도 해당 변수를 유지한다라는 것이다.

이 내용은 굳이 알아둘 필요는 없어 보이지만, 클로저의 내용물을 알아볼 수 있는 방법은 다음과 같다.

func.__closure__[idx].cell_contents

func은 클로저를 의미한다.
이러면 해당 클로저가 가지고 있는 idx번째 객체의 값을 알 수 있을 것이다. 쓸 일이 얼마나 있을지는 솔직히 잘 모르겠다.

 

그래서 이런 클로저를 언제 쓰느냐 라는 의문이 들 수 있다. 다른 좋은 쓰임새들도 있을 거라고 생각하지만, 개인적으로는 다음에 알아볼 내용인 데코레이터(decorator)에서 가장 잘 쓰이고 있다고 생각한다.

 

Categories:

Updated:

Comments