[Python] Closure
Closure
자신 영역 밖에서 호출된 함수의 변수 및 레퍼런스를 따로 세이브해둔 뒤, 접근할 수 있도록 해준다.
(=Free variable(자유변수)를 맵핑해준다.)
Free variable (자유변수) : 해당 블럭안에서 정의되지 않았지만, 사용한 변수
Form
-
중첩 함수를 가진다.
-
중첩 함수는 자신의 부모함수의 변수를 참조하고 있다. (Free variable)
-
부모함수는 중첩함수(자식함수)를 반환한다.
Example
def f(): # 부모함수
k = 777
def nested(): # 자식함수(중첩함수)
print(k)
return nested()
f() # 함수 f 실행
>>> 777
Code Analysis
-
f()
에 의해 함수f
가 실행된다. -
지역변수
k = 777
가 정의된다. -
nested 함수를 호출과 동시에 반환시킨다. (return)
-
이때 호출된 nested는 함수
f
(부모함수) 내부에서 정의된 중첩함수(자식함수)이며,k
를 출력시킨다.
여기서 4번째 단계가 뭔가 의문이 생긴다. nested 함수는 함수f
내부에서 정의되었다.
지역변수k에 nested 함수에서 접근이 가능할까? 결론적으로는 가능하다.(제한적으로)
nested함수에서 변수k는 자유변수이다. k가 자유변수로 인지됨에 따라 부모함수의 k값은 따로 저장되어지고,
nested함수에서 k가 호출되면 메모리 어딘가에 따로 저장되어있던 k의 값에 접근하는 것이다.
이 과정을 closure 라고 할 수 있다.
Notice
위에서 지역변수k에 nested함수가 접근이 제한적으로 가능하다고 한 이유를 다루어보겠다.
def f():
k = 777
def nested():
k = k + 3 # 자유변수 k에 새로운 값 할당
print(k)
return nested # 위 코드와는 다르게 () 제거하였다.
f()
>>> UnboundLocalError: local variable 'k' referenced before assignment
에러가 발생한다.
위와 다른점은 중첩함수 nested함수에서 자유변수 k
에 새로운 값을 할당하였다는 것이다.
이제 그 원인에 대해서 설명하겠다.
파이썬은 중첩함수에서 변수에 어떤값을 할당하려는 시도를 하면, 그 변수가 지역변수가 된다.
즉 k
에 k + 3
이라는 식을 할당하려고 시도하였기 때문에 k
는 자유변수가 아닌, 지역변수가 되어 k + 3
을 계산하는 과정에서 k
의 값을 찾지못하는 것이다.
nonlocal
위 문제를 해결하는 방법은 nonlocal
을 사용하는 것이다.
def f():
k = 777
def nested():
nonlocal k
k = k + 3
print(k)
return nested # 위 코드와는 다르게 () 제거하였다.
f()
>>> <function __main__.f.<locals>.nested>
에러가 발생하지 않았다.
a = f()
a()
>>> 780
a()
>>> 783
a()
>>> 786
문제없이 작동함을 알 수 있다.
Practice
def character(class_, hp):
def damaged(attack_damage):
nonlocal hp
hp -= attack_damage
return class_, hp
return damaged
first_ch = character("Warrior", 500)
second_ch = character("Wizard", 150)
Warrior는 500의 체력을, Wizard는 150의 체력을 가지도록 만들었다.
print(first_ch(30)) # Warrior 가 30의 데미지를 입었다.
print(second_ch(50)) # Wizard 가 50의 데미지를 입었다.
>>> ('Warrior', 470)
>>> ('Wizard', 100)
Leave a comment