파이썬 클로저 (closure)

정은경·2021년 3월 17일
0

함수를 클로저 형태로 만드는 방법의 예

  • c = calc()
  • c는 mul_add 함수 오브젝트를 가리킴! 이 함수 오브젝트가 클로저임!!

    클로저(closure)란?
    함수를 둘러썬 환경(지역변수, 코드 등)을 계속 유지하다가,
    함수를 호출할 때 다시 꺼내서 사용하하는 함수

  • 그래서 c()는 mul_add를 실행하는 것임
  • 즉, c()는 mul_add()와 동일
  • 그래서, c(x)는 mul_add(x)와 동일

여기서의 발견

  • 위의 예시를 보면, mul_add 함수는 외부 함수인 calc의 변수 a,b를 가져다 쓰고 있음
  • 이렇게 재사용?!할 내용을 outer 함수에다가 저장해두는 형태가, 마치 클래스의 속성변수?를 사용하는 것과 비슷해보임
  • 그래서, 위의 코드를 클래스로 바꿔 본다면
  • [참고] 클로저에 속한 지역변수는 바깥에서 직접 접근할 수 없으니깐, 클래스 "비공개" 속성변수로 정의해서 바꿔봄
class Calc():
    def __init__(self):
        self.__a = 3
        self.__b = 5
        
    def mul_add(self, x):
    	return self._a * x + self._b


c = Calc()
c.mul_add(1)
>>>> 8

여기서의 질문

  • 클래스형의 장점?은 내부 상태를 저장할 수 있다는 것 (ex. 내부변수)
  • 함수형 프로그램은 내부 상태를 따로 저장할 수 없음
  • 그런데 클로저는 내부 상태를 저장할 수 있음 (outer 함수에 저장가능)
  • 함수형 프로그래밍이 내부 상태를 저장할 수 없는 것을 클로저가 보완해주는 걸까?

클로저(closure)

  • 함수를 둘러썬 환경(지역변수, 코드 등)을 계속 유지하다가, 함수를 호출할 때 다시 꺼내서 사용하하는 함수
  • 클로저를 사용하면 프로그램의 흐름을 변수에 저장할 수 있음
  • 클로저는 지역변수와 코드를 묶어서 사용하고 싶을 때 활용
  • 클로저에 속한 지역변수는 바깥에서 직접 접근할 수 없으므로 데이터를 숨기고 싶을 때 활용

global과 nonlocal

>>> print(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> def foo():
...     global x
...     x = 1
...     print("foo!")
...     return x
...
>>>
>>> print(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>
>>> foo() # foo 함수 안에서 x를 global 변수로 선언함
foo!
1
>>>
>>> print(x) # foo 함수가 실행되자, x라는 글로벌 변수가 생겨서 x를 알게됨
1
>>>
  • global 키워드가 앞에있는 변수 x는 이제 global 변수가 된다
  • global 변수는 코드의 모든 범위에서 가져다 쓸 수 있는 변수가 된다
  • 위에서 처럼 global 변수 x가 없다면 global 변수 x를 만들어주고
  • global x가 이미 존재하면, global x를 가져온다
>>> x = 1
>>> print(x)
1
>>>
>>>
>>>
>>> def foo_out():
...     x = 11
...     y = 22
...     print(f"foo_out! x:{x} y:{y}")
...     def foo_in():
...         nonlocal y
...         y = 222
...         print(f"foo_in! : x:{x} y:{y}")
...     return foo_in
...
>>>
>>> print(x)
1
>>> print(y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>>
>>>
>>>
>>> type(foo_out)
<class 'function'>
>>> type(foo_out())
foo_out! x:11 y:22
<class 'function'>
>>> type(foo_out()())
foo_out! x:11 y:22
foo_in! : x:11 y:222
<class 'NoneType'>
>>>
>>>
>>>
>>> foo_out()
foo_out! x:11 y:22
<function foo_out.<locals>.foo_in at 0x7ffc8c3973a0>
>>> foo_out()()
foo_out! x:11 y:22
foo_in! : x:11 y:222
>>>
>>>
>>>
>>> print(x)
1
>>> print(y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>>
  • nolocal 키워드가 앞에있는 변수는 제일 가까운 외부에서부터 x를 찾아서, 그 x를 가져다가 사용한다
  • 주의!! nolocal은 함수1안에 함수2가 있고 함수3이 있는 등의 함수가 중첩되어 사용하는 경우에서 사용한다.
  • 그래서 함수3에 nonlocal 키워드가 있는 변수 x가 있다면
  • 변수 x를 함수2에서 찾고 없다면 함수1에서 찾는 식이다

Reference

profile
#의식의흐름 #순간순간 #생각의스냅샷

0개의 댓글