본문 바로가기
[파이썬]/함수

[파이썬][함수] 클로저 함수

by sung min_Kim 2023. 11. 15.
클로저

외부 함수의 변수를 참조하는 내부 함수


· 클로저


 클로저는 상위 함수에서 내부 함수를 반환하며, 외부 함수의 지역 변수를 참조하는 내부 함수를 클로저 함수라고 한다.
 
내부 함수는 자신이 생성될 때의 환경, 즉 상위 함수의 지역 변수를 '기억'하고 있는 상태가 된다.

 따라서 내부 함수가 호출될 때마다, 내부 함수는 외부 함수의 지역 변수를 '참조'하여 동작을 수행한다.
 
동작을 종료한 후에, 지역 변수는 메모리 상에서 해제되며, 언제든 호출될 수 있는 상태로서 남아있게 된다.


· 동작 원리

 

- 상위 함수에 지역 변수가 존재하는 경우
def outter_function(x):
	def inner_function(y):
    	sum = x + y
        return sum
    return inner_function


 'outter_function'은 상위 함수이며, 'inner_function'은 내부 함수이다. 해당 함수들은 각 각 별도의 메모리 주소를 가지고 있다.

 상위 함수를 호출하면 상위 함수의 코드 블록이 실행되고, 상위 함수의 지역 변수가 메모리에 할당된다.
 내부 함수의 코드 블록은 내부 함수가 직접 호출되었을 경우에만 실행이 되고, 상위 함수에 의해 리턴된 경우 상위 함수의 지역 변수를 참조할 수 있는 상태가 된다.

 상위 함수인 'outter_function'은 하위 함수를 리턴 함으로써 내부 함수를 동작한다.
 따라서 리턴된 내부 함수 'inner_function'는 상위 함수의 매개 변수 'x'를 참조할 수 있는 상태가 된다.
 
 내부 함수를 변수 'ex_func'에 할당하면, 해당 변수는 내부 함수의 메모리 주소를 참조할 수 있는 상태가 된다.

ex_func = outter_function(10)
print(ex_func) # <function outter_function.<locals>.inner_function at 0x7d5e34191e10>
               ## 내부 함수의 메모리 주소 출력
               ### ex_func 변수에 부여



 따라서, 'ex_func' 변수는 상위 함수의 매개 변수 'x'와 내부 함수 'inner_function'의 메모리 주소를 모두 참조하게 된다.
 상위 함수의 매개 변수 'x'에는 10이라는 값이 전달되어 대입된다.

 내부 함수를 호출하면 내부 함수의 코드 블록이 실행되고, 내부 함수의 매개 변수 'y'에 20이라는 값이 대입되어 연산을 수행하게 된다.

 내부 함수의 동작 결과, 리턴 값으로 '30'이 출력된다.

res_func = ex_func(20) # 30 출력

 



 파이썬의 특징 중, 한 가지 알아두어야 할 점은 '파이썬에서는 객체를 더 이상 사용하지 않으면 메모리를 자동으로 회수한다.'는 것이다. 함수도 이에 해당한다.

 호출한 함수의 동작이 끝나게 되면, 함수 내부의 지역 변수는 자동으로 메모리 상에서 해제된다.
 삭제되는 것은 아니고, 나중에 다시 호출하여 사용하기 전까지 메모리 상에서 잠시 해제하는 것일 뿐이다.

 그렇다면 위의 상위 함수인 'outter_function'는 어떻게 될까? 메모리 상에서 해제될까? 답을 미리 말하자면 '아니다'.
 함수는 언제든 호출될 수 있는 상태로 남아있기 때문에, 메모리 상에서 해제되지 않고 존재하는 상태로 남아 있는다.

 상위 함수의 매개 변수 'x'는 메모리 상에서 해제되겠지만 리턴한 내부 함수가 이를 참조하고 있으므로, 매개 변수 'x'는 해제되지 않고 메모리 상에 존재하고 있는 상태로 남아있게 된다.
 (함수의 매개 변수는 함수가 호출 될 때 생성되고, 함수가 종료될 때 사라지는 생명 주기를 가지며, 일반적으로 지역 변수로 취급한다.)

 따라서, 내부 함수인 'inner_function'는 외부 함수의 매개 변수인 'x'를 계속해서 참조할 수 있다. 


 

- 상위 함수에 지역 변수가 존재하지 않는 경우
def outter_function():
	x = 0
	def inner_function(y):
    	sum = x + y
        return sum
    return inner_function

 전의 예시와는 다르게 상위 함수가 매개 변수를 가지지 않고, 지역 변수인 'x'를 가지고 있다.
 해당 지역 변수는 '0'을 디폴트 값으로 가지고 있다.

 상위 함수를 호출하여 동작하면 'x = 0'이라는 값을, 리턴한 내부 함수 'inner_function'에서 참조할 수 있게 된다.
 리턴된 내부 함수는 상위 함수의 지역 변수 'x'를 참조하며, '0'이라는 값이 'x'에 대입된다.


ex_func = outter_function() 
print(ex_func) # <function outter_function.<locals>.inner_function at 0x7d5e51cbdbd0> 출력

  상위 함수를 호출하여 변수에 할당하게 되면, 해당 변수는 상위 함수의 지역 변수 'x'와 내부 함수의 메모리 주소를 참조할 수 있는 상태가 된다.


res_func = ex_func(20)
print(res_func) # 20 출력

 내부 함수를 호출하면, 내부 함수의 코드 블록이 동작하고, 'x + y'의 연산 결과가 지역 변수 'sum'에 할당된다.

 'x'는 상위 함수의 지역 변수 'x'를 참조한 값이 대입되고, 'y'는 20이라는 값을 넘겨받아 해당 값이 대입된다.
 리턴된 지역 변수 'sum'은 연산 결과를 반환하고, 이후 메모리 상에서 해제되게 된다. 


 다만, 상위 함수의 지역 변수 'x'의 값을 수정하기 위해서는 'nonlocal' 키워드를 사용하여 그 값을 변경할 수 있다.

def outter_function():
  total = 0
  def inner_function(num):
    nonlocal total
    total += num
    print(f"총 합 : {total}")
    return total
  return inner_function

 'nonlocal' 키워드는 상위 함수의 지역 변수를 하위 함수 내부에서 값을 수정하여 사용할 수 있게 하여 준다.
 따라서 내부 함수를 호출하면 내부 함수의 'num'을 중첩하여 연산을 수행한 결과를 지역 변수 'total'에 할당한다.
 이로써 상위 함수의 지역 변수인 'total'의 값이 변경되는 것을 확인할 수 있다.


ex_func = outter_function()
print(ex_func) # 'ex_func' 변수에 내부 함수 부여

 'ex_func' 변수에 내부 함수를 부여하여, 내부 함수를 참조할 수 있는 상태로 만든다.


res_func = ex_func(10)
print(res_func) # 총합 : 10씩 값이 증가하여 출력

 내부 함수의 매개변수 'num'에 '10'의 값을 넘겨주며, 이를 대입하여 내부 함수의 코드 블록을 동작한다.
 이로써, 내부 함수를 호출 할 때마다 '10'의 값을 중첩하여 더하는 연산을 수행하여 'total'을 반환하게 된다. 


 

- 조건부 클로저 함수 프로그래밍
def outter_function(condition):
  def true_case():
  	print("참인 경우에만 동작 수행")
  def false_case():
  	print("거짓인 경우에만 동작 수행")
    
  rs = true_case if condition else false_case
  
  return rs

 조건을 부여하여, 결과에 따른 동작을 수행하는 클로저 함수를 구성할 수 있다.
 상위 함수인 'outter_function'는 지역 변수 'rs'를 반환한다.
 리턴된 'rs'는 'condition'의 메모리 주소를 참조하여 조건 결과에 따라 'true_case' 함수 또는 'false_case' 함수를 실행한다.


ex_func = outter_function(True)

 'ex_func' 변수에 내부 함수를 부여하여, 해당 함수와 해당 함수가 참조하고 있는 상위 함수의 매개 변수인 'condition'의 메모리 주소를 참조할 수 있도록 한다.


 상위 함수의 매개 변수인 'condition'은 'True'를 인자로 건네 받아 이를 사용한다.
 이에 따라 리턴된 지역 변수 'rs'는 'True'의 값을 가지게 되며, 조건에 부합하는 true_case를 반환한다.

 최종적으로 'true_case' 함수가 작동하며, 코드 블록 내의 print문을 실행하게 된다.

res_func = ex_func()
print(res_func) # 참인 경우에만 동작 수행 출력

 

 

 


클로저 함수에 대해  알아보았다.

클로저는 상위 함수에서 반환된 내부 함수를 나타내며,
리턴된 내부 함수는 상위 함수에 위치한 지역 변수의 메모리 주소를 참조할 수 있는 상태가 된다.

이로서 상위 함수를 호출하였을 때 리턴된 내부 함수가 동작하며,
해당 함수는 상위 함수 내의 지역 변수를 메모리 상에서 참조할 수 있게 된다.

메모리 상의 주소를 생각하며 구조를 이해하도록 노력하여 보자.

'[파이썬] > 함수' 카테고리의 다른 글

[파이썬][함수] 데코레이터 함수  (2) 2023.11.15
[파이썬] 람다 함수  (0) 2023.11.09
[파이썬] 함수  (0) 2023.11.08