기본 콘텐츠로 건너뛰기

벡터와 행렬에 관련된 그림들

[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' 와 같...

[sympy] Sympy객체의 표현을 위한 함수들

Sympy객체의 표현을 위한 함수들 General simplify(x): 식 x(sympy 객체)를 간단히 정리 합니다. import numpy as np from sympy import * x=symbols("x") a=sin(x)**2+cos(x)**2 a $\sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$ simplify(a) 1 simplify(b) $\frac{x^{3} + x^{2} - x - 1}{x^{2} + 2 x + 1}$ simplify(b) x - 1 c=gamma(x)/gamma(x-2) c $\frac{\Gamma\left(x\right)}{\Gamma\left(x - 2\right)}$ simplify(c) $\displaystyle \left(x - 2\right) \left(x - 1\right)$ 위의 예들 중 객체 c의 감마함수(gamma(x))는 확률분포 등 여러 부분에서 사용되는 표현식으로 다음과 같이 정의 됩니다. 감마함수는 음이 아닌 정수를 제외한 모든 수에서 정의됩니다. 식 1과 같이 자연수에서 감마함수는 factorial(!), 부동소수(양의 실수)인 경우 적분을 적용하여 계산합니다. $$\tag{식 1}\Gamma(n) =\begin{cases}(n-1)!& n:\text{자연수}\\\int^\infty_0x^{n-1}e^{-x}\,dx& n:\text{부동소수}\end{cases}$$ x=symbols('x') gamma(x).subs(x,4) $\displaystyle 6$ factorial 계산은 math.factorial() 함수를 사용할 수 있습니다. import math math.factorial(3) 6 a=gamma(x).subs(x,4.5) a.evalf(3) 11.6 simpilfy() 함수의 알고리즘은 식에서 공통사항을 찾아 정리하...

sympy.solvers로 방정식해 구하기

sympy.solvers로 방정식해 구하기 대수 방정식을 해를 계산하기 위해 다음 함수를 사용합니다. sympy.solvers.solve(f, *symbols, **flags) f=0, 즉 동차방정식에 대해 지정한 변수의 해를 계산 f : 식 또는 함수 symbols: 식의 해를 계산하기 위한 변수, 변수가 하나인 경우는 생략가능(자동으로 인식) flags: 계산 또는 결과의 방식을 지정하기 위한 인수들 dict=True: {x:3, y:1}같이 사전형식, 기본값 = False set=True :{(x,3),(y,1)}같이 집합형식, 기본값 = False ratioal=True : 실수를 유리수로 반환, 기본값 = False positive=True: 해들 중에 양수만을 반환, 기본값 = False 예 $x^2=1$의 해를 결정합니다. solve() 함수에 적용하기 위해서는 다음과 같이 식의 한쪽이 0이 되는 형태인 동차식으로 구성되어야 합니다. $$x^2-1=0$$ import numpy as np from sympy import * x = symbols('x') solve(x**2-1, x) [-1, 1] 위 식은 계산 과정은 다음과 같습니다. $$\begin{aligned}x^2-1=0 \rightarrow (x+1)(x-1)=0 \\ x=1 \; \text{or}\; -1\end{aligned}$$ 예 $x^4=1$의 해를 결정합니다. solve() 함수의 인수 set=True를 지정하였으므로 결과는 집합(set)형으로 반환됩니다. eq=x**4-1 solve(eq, set=True) ([x], {(-1,), (-I,), (1,), (I,)}) 위의 경우 I는 복소수입니다.즉 위 결과의 과정은 다음과 같습니다. $$x^4-1=(x^2+1)(x+1)(x-1)=0 \rightarrow x=\pm \sqrt{-1}, \; \pm 1=\pm i,\; \pm1$$ 실수...