기본 콘텐츠로 건너뛰기

[ML] 결정트리(Decision Tree) 모델

[python] 클로저(Closure)

클로저(Closure)

다음 코드의 함수 first()중첩함수increse()를 포함하는 외곽함수입니다.

def first(x):     #(1)
    def increase(y):  #(3)
        return (x+y)   #(4)
    return increase(x) #(2)

first(1)
2

위 코드에서 두 개의 변수 xy는 각각 비지역변수 그리고 지역변수가 됩니다. 이 함수의 동작과정은 다음과 같습니다.

  1. first 함수에 인수 x 값 전달
  2. 본문에서 중첩함수를 사용할 동인이 없으므로 바로 return() 함수를 실행
    • 인수 x에 1을 전달하면서 중첩함수 increase() 호출
  3. 중첩함수에 전달된 인수 x는 지역변수 y값이 됩니다. 그러므로 xy 모두 1이 됩니다.
  4. 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)로 동작됩니다.

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b