클로저(Closure)
다음 코드의 함수 first()는 중첩함수인 increse()를 포함하는 외곽함수입니다.
def first(x): #(1) def increase(y): #(3) return (x+y) #(4) return increase(x) #(2) first(1)
2
위 코드에서 두 개의 변수 x와 y는 각각 비지역변수 그리고 지역변수가 됩니다. 이 함수의 동작과정은 다음과 같습니다.
- first 함수에 인수 x 값 전달
- 본문에서 중첩함수를 사용할 동인이 없으므로 바로
return()
함수를 실행- 인수 x에 1을 전달하면서 중첩함수 increase() 호출
- 중첩함수에 전달된 인수 x는 지역변수 y값이 됩니다. 그러므로 x와 y 모두 1이 됩니다.
- increase() 함수 실행으로 비지역변수(x)와 지역변수(y)의 합을 실행
사실 위 함수는 x의 선언에 의해 y가 결정되는 구조로 다음과 같이 간단히 작성할 수 있습니다.
def first0(x): return (x+x) first0(1)
2
위와 같이 지역변수가 비지역변수에 의해 결정되는 구조에서는 중첩함수에 인수를 독립적으로 전달 또는 변경할 수 없습니다. 이것은 중첩함수의 인수가 외부함수 내부에서 선언되고 그 인수가 외부함수의 인수에 좌우되기 때문입니다. 대신에 비지역과 지역 변수가 외부함수의 호출과 함께 동시에 전달된다면 이 두 변수 값들은 독립적으로 사용될 수 있습니다. 그러나 함수 first()에 전달할 인수가 1개이므로 first()함수 구조로 중첩함수 increase()내의 인수를 독립적으로 적용할 수 없습니다. 대신에 다음에 정의한 first1() 함수의 경우 중첩함수의 인수를 독립적으로 사용할 수 있습니다.
def first1(x): def increase(y): return (x+y) return increase first1(1)
<function __main__.first1.<locals>.increase(y)>
위 명령은 외부함수인 first(1)을 실행하는 과정에서 중첩함수 increase를 호출한 상태지만 그 호출된 함수에 인수 전달 방법이 누락된 상태입니다. 결과적으로 지역변수의 값을 전달할 수 없으므로 평가될 수 없습니다.
함수는 일급객체이므로 다른 변수에 할당할 수 있습니다. 그러므로 위 함수 first1()이 increase를 반환하는 과정은 다음과 같이 새로운 변수에 함수를 할당하는 것으로 간주할 수 있습니다.
first1()= increase
그러므로 first1() 함수는 increase()의 모든 내용을 포함하고 있으므로 이 함수의 인수를 전달받을 수 있습니다. 즉, first(1)()와 같이 중첩함수의 인수를 외곽함수의 실행과 함께 전달할 수 있으므로 지역변수를 외부에서 조절할 수 있습니다. 이와 같이 함수가 다른 함수의 인수를 전달받을 수 있는 상태를 클로저(closure)라고 합니다.
first1(1)(2)
3
파이썬에서 생성되는 객체는 다양한 내장 매소드 또는 속성들이 부여됩니다. 이들 중 __closure__
은 클로저의 기능을 활성시키는 속성입니다. 위 코드에서 생성한 first1(1)은 중첩함수의 인수가 전달되지 않은 상태이므로 외부로부터 이 인수를 기다리는 상태입니다. 그러므로 클로저(closure) 상태입니다. 이 상태에 있는 객체는 내장 속성 __closure__를 포함합니다. 그러나 중첩함수의 인수가 전달되어 최종 평가가 이루어진 상태에서는 더이상 클로저가 아니므로 이 속성은 포함되지 않을 것입니다.
x=first1(1) "__closure__" in dir(x)
True
y=x(2);y
3
"__closure__" in dir(y)
False
다음은 여러 수들의 합을 계산하는 함수로 중첩함수 total()를 포함하고 있습니다. 함수는 sum()는 중첩함수의 이름객체를 반환합니다. 다시말하면 total은 외과함수인 sum()에 할당됩니다. 그러므로 sum()이 평가되기 위해서는 중첩함수의 인수들이 전달되어야 하므로 최종평가되기전 함수 sum()은 클로저이며 이를 나타내는 내장속성인 __closure__
를 포함합니다.
def sum(x): def total(*y): re=x for i in y: re += i return (re) return total sum(1)(2,3,4,5,6,7, 8, 9, 10)
55
"__closure__" in dir(sum)
True
속성 __closure__
의 필요는 중첩함수의 인수를 전달하는 경로에 의해 결정하는 것으로서 그 속성이 필요하다는 것은 객체의 평가되지 않는 상태임을 나타냅니다. 그러므로 위 객체 sum(1)(2,3,4,5,6,7, 8, 9, 10)와 같이 모든 인수가 전달된 상태에서는 __closure__는 불필요합니다. 즉, 평가된 객체의 경우는 클로저가 아닙니다.
"__closure__" in dir(sum(1))
True
"__closure__" in dir(sum(1)(2,3,4,5,6,7, 8, 9, 10))
False
다음은 여러개의 중첩함수를 사용하여 각 변수의 적용범위를 한정시킬 수 있습니다.
def first2(a): def second(b): def third(c): return (a+b+c) return third return second
first2(1)(1)(1)
3
다음은 중첩함수로서 람다함수을 적용한 예입니다. 즉, 함수의 결과를 반환하는 return()
함수에 람다함수를 전달함으로서 클로저(closure)를 생성할 수 있습니다. 다음 코드에서 plus() 함수내에서 선언된 람다 함수는 외부함수 호출시 전달된 인수를 기반으로 계산됩니다.
람다함수(Lambda Function)는 익명함수로 함수의 이름이 없습니다. 즉, 함수를 할당받는 이를 객체가 없는 상태이므로 다음과 같이 외곽함수에서 람다함수 자체를 반환하는 구조로 클로저를 생성할 수 있습니다.
def plus(n): return lambda x: x+n plus3=plus(3) plus7=plus(7) plus3(4), plus3(10)
(7, 13)
위 코드에서 객체 plus3, plus7은 외부함수의 인수만 전달한 객체로 중첩함수의 인수 전달을 기다리고 있는 상태입니다. 각 객체를 평가하기 위해 호출시 그 인수를 입력합니다. 즉, 내부에 정의되지 않은 변수들은 주변이나 이미 외부로부터 정의된 변수를 기억하는 클로저(closure)로 동작됩니다.
댓글
댓글 쓰기