Python - Closure

Enjoywater·2020년 8월 23일
0

TIL

목록 보기
7/33
post-thumbnail

Closure

wikipedia에 정의된 내용을 보면

first-class functions의 개념을 이용하여 scope에 묶인 변수를 바인딩 하기 위한 일종의 기술이다.
기능상으로, 클로저는 함수를 저장한 레코드(record)이며, scope의 인수들은 클로저가 만들어질 때 정의되며, 스코프 내의 영역이 소멸되었어도 그에 대한 접근은 독립된 복사본인 클로저를 통해 이루어질 수 있다.

이라고 어렵게 나와있다.

다른말로 써보면,
중첩함수부모함수변수정보를 가두어 사용하는 것을 말한다.

부모함수중첩함수리턴하게 되는데,
이와 같은 방식을 사용하면 부모함수변수를 외부로부터 직접적인 접근은 격리하면서
중첩함수를 통해 격리된 변수를 사용한 연산이 가능하게 된다.

  • 중첩함수부모함수변수나 정보중첩함수 내에서 사용한다.
  • 부모함수중첩함수return한다.
  • 부모함수에서 return했으므로 부모함수변수는 직접적인 접근이 불가능 하지만 부모함수return중첩함수를 통해서 사용될 수 있다.

코드를 통한 간단한 예제로 위 말들을 풀어보겠다.

def greeting():
    name = "Water"
    def inner():
        print("Hi" + " " + name)
    return inner

result = greeting()
result()

greeting함수는 inner함수를 가지고 있는 부모함수이며 inner함수를 return한다.
name이라는 변수를 선언하고 있으며, inner함수는 해당 변수를 가져와 출력한다.

greeting함수를 result라는 변수에 담은 후, result를 호출해보면

Hi Water

라는 결과값이 나오게된다.
result라는 변수가 greeting속 name변수를 사용해 호출에 성공한 것 이다.

이미 greeting함수는 result 변수를 선언할 때 호출되었는데 어떻게 성공한 것 일까?

그에 대한 해답은 Closure에 있다.

다시 코드로 돌아가서 result를 출력해보면

print(result)

# 결과값 -> <function greeting.<locals>.inner at 0x7f587c7320d0> 

로 inner함수가 할당되어 있는 것을 알 수 있다.

조금 더 깊게 들어가보면,

print(dir(result))
결과값 ->

['__annotations__', '__call__', '__class__', '__closure__', '__code__', 
'__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__get__', '__getattribute__', '__globals__', 
'__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', 
'__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']

result의 namespace를 조회해보니 __closure__라는 이름이 들어있다.
__closure__의 type을 조회해보면 튜플이 나오며, cell이라는 문자열 오브젝트를 가지고있다.

print(dir(result.__closure__[0])) #cell
결과값 ->

['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', 
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', 
'__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']

cell속에는 cell_contents라는 속성이 있다.
바로 cell_contents속에

print(result.__closure__[0].cell_contents)

# 결과값 -> Water

name변수에 할당되었던 Water라는 문자열이 들어있다.
result에는 inner함수가 할당되어 있으며, 어딘가에 Water라는 값을 저장해서 사용한다는 것을 알 수 있다.


위에서 정리했던 3가지 Closure의 내용에 하나하나 대입해 본다면,

  • 중첩함수부모함수변수나 정보중첩함수 내에서 사용한다.
    ex - inner함수가 name변수를 사용한다.

  • 부모함수중첩함수return한다.
    ex - greeting함수가 inner함수를 리턴한다.

  • 부모함수에서 return했으므로 부모함수변수는 직접적인 접근이 불가능 하지만 부모함수return중첩함수를 통해서 사용될 수 있다.
    ex - result에 inner함수가 할당되며 inner함수를 통해 부모함수의 변수에 접근해서 name 사용한다.

로 정리가 될 수 있다.

다양한 예시

  1. 내부함수 parameter
def greeting():
    def inner(name):
        print("Hi" + " " + name)
    return inner
 
result = greeting()

result("Enjoy")
result("Water")
result("EnjoyWater")

# 결과값 -> Hi Water
#         Hi Enjoy
#         Hi EnjoyWater

greeting함수에는 아무 parameter도 없지만 inner함수의 parameter값으로 다양한 출력이 가능하다.

  1. 부모함수 parameter + 내부함수 parameter
def greeting(greet):
    def inner(name):
        print(greet + " " + name)
    return inner
sayHello = greeting("Hello")
sayHello("Enjoy")

sayHi = greeting("Hi")
sayHi("Water")

# 결과값 -> Hello Enjoy
#         Hi Water

이처럼 Closure를 활용하면 다양하게 함수를 변형하며 사용할 수 있게된다.

profile
블로그 이전 👉🏻 enjoywater.tistory.com

0개의 댓글