중첩함수(nested function)
함수 안에 함수를 정의한 것을 말합니다.
중첩함수를 통해 default로 enclosing function local scope 변수를 참조할 수 있습니다.
내부 함수와 외부 함수
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
print msg
return inner_func
inner_func을 내부함수, outer_func를 외부함수라고 합니다.
inner_func 입장에서 outer_func 함수의 지역변수 영역을 enclosing function local scope(바깥 함수 지역 변수 영역) 라고 부릅니다.
클로저(closure)란
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
print msg
return inner_func
my_func = outer_func("Megumin is so cute!!")
my_func()
Megumin is so cute!!
내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가리킵니다.
inner_func가 outer_func의 지역변수인 msg를 참조하여 출력하는 것을 볼 수 있습니다.
함수를 변수에 저장할 수 있는 이유는 파이썬에서는 함수가 일급 객체(first-class object)이기 때문입니다.
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
print msg
return inner_func
first_func = outer_func("Megumin is so cute!!")
first_func()
second_func = outer_func("It's true")
second_func()
print
print "outer_func : ", id(outer_func), "\n"
del outer_func
first_func()
second_func()
print
print "first_func : ", id(first_func)
print "second_func : ", id(second_func)
Megumin is so cute!!
It's true
outer_func : 37766872
Megumin is so cute!!
It's true
first_func : 37766984
second_func : 37767096
outer_func을 삭제해도 first_func가 정상적으로 호출됨을 알 수 있습니다.
이는 함수를 변수에 저장할 때마다 객체를 새롭게 생성하기 때문입니다.
(outer_func, first_func, second_func 모두 id 값이 다릅니다)
즉, first_func와 second_func는 서로 완전히 독립된 객체인 것입니다.
클로저 내에서 enclosing function local scope 변수 Read/Write
클로저에서 enclosing function local scope 변수에 접근할 수 있다면 modify 하는 것도 가능하지 않을까라고 생각할 수 있습니다.
하지만 그것은 다음과 같은 오류를 보여줄 뿐입니다.
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
print msg
msg = "Awesome!!"
print msg
return inner_func
first_func = outer_func("Megumin is so cute!!")
first_func()
UnboundLocalError: local variable 'msg' referenced before assignment
첫 번째 print문에서 inner_func 함수 내에서 msg라는 변수는 선언된 적이 없기 때문에 enclosing function local scope에서 찾게 됩니다. 하지만 이 변수는 전역 변수와 마찬가지로 함수 내에서 수정할 수 없습니다. (파이썬 네임스페이스 참고)
그래서 python 3.x 부터는 nonlocal 이라는 키워드를 통해 외부함수 영역의 변수를 modify할 수 있도록 합니다.(nonlocal은 global과 비슷한 개념이라 할 수 있습니다)
python 3.x
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
print(msg)
nonlocal msg
msg = "Awesome!!"
print(msg)
return inner_func
first_func = outer_func("Megumin is so cute!!")
first_func()
Megumin is so cute!!
Awesome!!
다만, 별로 추천하지는 않다고 합니다.(보충 필요)
대신, python 2.x 에서는 내부 함수의 local variable에 enclosing scope의 변수를 저장하여 사용할 수 있습니다.
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
_msg = msg
print _msg
_msg = "Awesome!!"
print _msg
return inner_func
first_func = outer_func("Megumin is so cute!!")
first_func()
Megumin is so cute!!
Awesome!!
또는 함수 속성에 변수를 대입해도 가능합니다.
# -*- coding: utf-8 -*-
def outer_func(msg):
def inner_func():
inner_func.msg = msg
print inner_func.msg
inner_func.msg = "Awesome!!"
print inner_func.msg
return inner_func
first_func = outer_func("Megumin is so cute!!")
first_func()
Megumin is so cute!!
Awesome!!
왜 사용할까
● 전역 변수의 사용을 피할 수 있음
● private 속성이 없는 파이썬에서 내부 데이터의 은닉을 할 수 있음
● 하나의 함수로 여러 가지의 함수를 간단히 만들어 낼 수 있도록 해줌
● 기존에 만들어진 함수나 모듈 등을 수정하지 않고도 wrapper 함수를 이용해 커스터마이징할 수 있게 해줌
(상세 설명 보충 필요)
요약
● 클로저란 내부 함수 영역에서 외부 함수 영역의 변수에 접근하는 것을 말함
● 파이썬에서 클로저가 구현 가능한 이유는 함수를 일급 객체로 취급하기 때문
● 클로저 내에서 외부 함수 영역 변수를 수정하기 위해서는 별도의 방법이 필요함
● 데이터 은닉 등의 목적을 위해 사용함
참고
[중첩함수]
https://www.programiz.com/python-programming/closure
[내부 함수와 외부 함수]
http://blog.naver.com/msyang59/220773717531
[클로저란]
https://slipp.net/questions/249
https://opentutorials.org/course/743/6544
http://jonnung.blogspot.kr/2014/09/python-easy-closure.html
[클로저 내에서 enclosing function local scope 변수 Read/Write]
[왜 사용할까]
http://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%B4%EB%A1%9C%EC%A0%80-closure/