ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL. 29 전역 변수, 지역변수, 클로저 알아보기
    TIL 2020. 11. 6. 21:56
    728x90

    ## 클로저를 알기 위해서는 먼저, 함수의 사용 범위를 먼저 알아보자.

    ## 전역 함수와 지역 함수

    ## 전역 변수 (global variable) == 위처럼 함수를 포함하여 스크립트 전체에서 접글 할 수 있는 변수를 전역 변수라 한다.

    # 특히, 전역 번수에 접근 할 수 있는 범위를 전역 범위 (global scope)라고 한다.

    x = 10 # 전역 변수
    def foo():
        print(x) # 전역 변수 출력
    
    foo()
    
    print(x) # 전역 변수 출력
    ::
    10

    ## foo 함수에 x라는 변수가 업지만, 전역 변수로 x가 있으므로 foo 함수에서도 x 변수를 사용할 수 있다.


    ## 지역 변수 (local variable) == 지역 변수 영역이 정해진 변수로 변수를 만든 함수 안에서만 접근이 가능하며

         함수 바깥에서는 접근 할 수없다.

    # 특히, 지역 변수에 접근 할 수 있는 범위를 지역 범위 (local scope)라고 한다.

    def foo():
        x = 10 # foo의 지역 변수
        print(x) # foo의 지역 변수 출력
    
    foo()
    ::
    10
    
    print(x) # error . foo의 지역 변수는 출력할 수 없음
    

    # 위의 print(x)는 error ==> 지역 변수로만 존재하는 x에 접근 할 수 없다.

    ## print 함수가 지역 범위에 없기 때문이다.


    ## 전역 변수 x 와 지역 변수 x는 같을까?

    x = 10 # 전역 변수
    def foo():
        x = 20 # x는 foo의 지역 변수 
        print(x) # foo의 지역 변수 출력
    
    foo()
    ::
    20
    
    print(x) # 전역 변수 출력
    ::
    10

    ## foo() 출력값은 지역 변수 x 값인 20을 출력하고, print(x)는 전역 변수 x값인 10을 출력한다.

    ## 여기서 전역 변수 x 와 지역 변수 x 는 "이름만 같을 뿐 서로 다른 변수로, 각 값은 서로 다른 메모리에 저장된다."


    ## 함수 안에서 전역 변수 변경하기

    ## 함수 안에서 전역 변수의 값을 변경할 수 있다.

    # global 전역변수

    x = 10 # 전역 변수
    def foo():
        global x # 전역 변수 x를 사용하겠다고 설정
        x = 20 # x는 전역 변수 , x = 20이 다시 할당되는 걸로 이해
        print(x) # 전역 변수 출력
    
    foo()
    ::
    20
    print(x) # 전역 변수 출력
    ::
    20

    ## global x 라는 뜻은 여기서 부터 전역 변수 x를 사용하겠다는 뜻으로 해석해도 된다.

    ## 따라서, global x 를 작성한 이후의 변수 x는 전역 변수 x를 의미한다.


    ## 전역 변수가 없을때의 global

    # 전역 변수 x가 없는 상태
    
    def foo():
        global x # x를 전역 변수로 만듦
        x = 20 # x는 전역 변수
        print(x) # 전역 변수 출력
    
    foo()
    ::
    20
    print(x) # 전역 변수 출력
    ::
    20

    ## 전역 변수가 아무것도 없을때 global 를 사용할 경우 해당 변수는 전역 변수가 된다.

    # x는 전역 변수가 된다.


    ## 함수 안에서 함수 만들기

    ## def 를 한 함수안에서 계속 사용하여 함수 안에 함수를 만들 수 있다.

    # def 함수이름1():

    # 코드

    # def 함수이름2():

    # 코드

    def print_hello():
        hello = 'Hello, world!'
        def print_message():
            print(hello)
        print_message()
    
    print_hello()
    ::
    
    'Hello, world!'

    ## 함수 호출시 작동 순서를 본다면 // 1줄, 2줄, 5줄, 3줄, 4줄 로 표현 할 수 있다.

    ## 위 함수에서 보면 print_messge 함수 입장에서는 자신이 사용할 수 있는 변수 hello가 없다.

    ## 그런데도 error가 나지 않는 이유"바깥쪽 함수의 지역 변수는 그 안에 속한 모든 함수에서 접근할 수 있는 특징"때문이다.

     


    ## 바깥쪽 함수의 지연 변수를 안쪽 함수에서 변경하기

    def A():
        x = 10 # A의 지역 변수 x
        def B():
            x = 20 # x에 20 할당
        B()
        print(x) # A의 지역 변수 x 출력
    
    A()
    ::
    10

    ## 위 함수에서 중요 포인트는 print가 어떤 함수에 위치해 있는가 이다.

    ## 얼핏 보기에는 B 함수의 지역 변수 20을 출력해야할 것 같지만,

          print 함수가 A 함수에 위치해 있기 때문에 A의 지역변수 10을 출력한다.

    ## 파이썬에서는 함수 에서 변수를 만들면 그 변수는 항상 해당 함수의 지역 변수가 된다.

    def A():
        x = 10 # A의 지역 변수 x
        def B():
            x = 20 # x에 20 할당
            print(x)
        B()
    
    A()
    ::
    20

    ## 위와 같이 print 함수를 B 함수에 위치시키면 B의 지역변수인 20을 출력한다.


    ## 현재 함수에서 바깥쪽 함수의 지역 변수 변경하기

    # nonlocal 지역변수

    def A():
        x = 10 # A의 지역 변수 x
        def B():
            nonlocal x # 현재 함수의 바깥쪽에 있는 지역 변수 사용
            x = 20 # A의 지역 변수 x에 20 할당
        B()
        print(x) # A의 지역 변수 x 출력
    
    A()
    ::
    20

    ## nonlocal x 를 이용해 현재 함수인 B 함수에서 바깥쪽에 있는 (A함수)의 지역 변수를 변경 할 수 있다.

    ## global 과 비슷하게 nonlocal x라는 뜻은 여기서 부터 바깥쪽 함수의 지역 변수 x를 사용하겠다는 뜻으로 해석

    ## 따라서 nonlocal x 를 작성한 이후의 변수 x는 바깥쪽 함수의 지역 변수 x를 의미힌다.

    ## nonlocal은 현재 함수의 지역 변수가 아니라는 뜻이며 바깥쪽 함수의 지역 변수를 사용한다.


    ## nonlocal 의 변수를 찾는 순서

    def A():
        x = 10
        y = 100
        def B():
            x = 20
            def C():
                nonlocal x # B 의 지역 함수 x = 20
                nonlocal y # A 의 지역 한수 y = 100
                x = x + 30
                y = y + 300
                print(x)
                print(y)
            C()
        B()
    
    A()
    ::
    50
    400

    ## nonlocal 은 바깥쪽 지역함수를 찾을때 가장 가까운 함수에서의 지역 함수 값을 찾는다.

    ## 위 nonlocal y --> B의 지역 함수 y가 있었다면 B 함수의 지역 함수 y를 사용하였을것이다.

    ## 실무에서는 이렇게 여러 단계로 함수를 만들 일은 거의 없다.

    ## 그리고 함수마다 이름이 같은 변수를 사용하기 보다는 변수 이름을 다르게 짓는 것이 훨씬 좋다.


    ## global로 전역 변수 사용하기

    x = 1
    def A():
        x = 10
        def B():
            x = 20
            def C():
                global x
                x = x + 30
                print(x)
            C()
        B()
     
    A()
    ::
    31

    ## 함수가 몇개가있던 전역 변수 x =1 을 사용한다.

    ## 파이썬에서 global을 제공 "하지만" 함수에서 값을 주고받을 때는 매개변수와 반환값을 사용하는 것이 좋다.

    ## 특히 전역 변수는 코드가 복잡해졌을 때 변수의 값을 어디서 바꾸는지 파악하기 힘들다.

    ## 따라서 전역 변수는 가급적이면 사용하지 않는 것을 권장한다.

    ## 클로저 사용하기

    ## 함수를 클로저 형태로 만드는 방법

    def calc():
    	a = 3
    	b = 5
    	def mul_add(x):
    		return a * x + b # 함수 바깥쪽에 있는 지역 변수 a, b를 사용하여 계산
    	return mul_add # mul_add 함수를 반환
    
    c = calc()
    
    print(c(1), c(2), c(3), c(4), c(5))
    ::
    8 11 14 17 20
    

    # 함수 바깥쪽에 있는 지역 변수 a, b를 사용하여 a * x + b를 계산하는 함수 mul_add를 만든 뒤에 함수 mul_add 자체를 반환.

    ## 함수 mul_add를 만든 뒤, 이 함수를 바로 호출하지 않고 return으로 함수 자체를 반환

    ## (함수를 반환할 때는 함수 이름만 반환해야 하며 ( )(괄호)를 붙이면 안 됩니다).

    c = calc()
    
    print(c(1), c(2), c(3), c(4), c(5))
    ::
    8 11 14 17 20

     

    ## 위와 같이 calc 함수를 호출한뒤 반환값을 c에 할당한다.

    ## calc에서 mul_add 를 반환하였으므로 c에는 mul_add가 들어가게 된다.

    ## 따라서 c(인수) 형태로 호출해보면 mul_add의 a*xb의 계산식을 사용할 수 있게된다.

    ## 또한, 자세히 보면 함수 c = calc() 에서 calc 함수는 1번 사용하고 끝났음에도 불구하고

    ## c 는 calc의 지역함수 a, b 를 계속 사용하는 것을 볼 수 있다.

    ## 이렇게 함수를 둘러싼 환경(지역 변수, 코드 등)을 계속 유지하다가,

    ## 함수를 호출할 때 다시 꺼내서 사용하는 함수를 클로저(closure)라고 한다.

    ## 여기서는 c에 저장된 함수가 클로저이다.

     


    ## 클로저를 사용하는 이유

    # 1. 지역 변수와 코드를 묶어서 사용하고 싶을 때 활용한다

    # 2. 또한, 클로저에 속한 지역 변수는 바깥에서 직접 접근할 수 없으므로 데이터를 숨기고 싶을 때 활용.

    추가로 클로저를 사용하면 프로그램의 흐름을 변수에 저장 할 수 있다


    ## lambda로 클로저 만들기

    def calc():
    	a = 3
    	b = 5
    	return lambda x: a * x + b # 람다 표현식을 반환
    
    c = calc()
    
    print(c(1), c(2), c(3), c(4), c(5))
    ::
    8 11 14 17 20

    ## 람다 표현식 자체를 반환한것으로 람다 표현식을 이용하면 좀더 간단하게 코드를 작성할 수 있다.

    ## 보통 클로저는 람다 표현식과 함께 사용하는 경우가 많아 둘을 혼동하기 쉬운데

    ## 람다는 이름이 없는 익명 함수를 뜻하고, 클로저는 함수를 둘러싼 환경을 유지했다가 나중에 다시 사용하는 함수를 뜻한다.


    ## 클로저의 지역 변수 변경하기

    def calc():
    	a = 3
    	b = 5
    	total = 0
    	def mul_add(x):
    		nonlocal total
    		total = total + a * x + b
    		print(total)
    	return mul_add #함수를 바로 호출하지 않고 리턴으로 함수 자체를 반환함 () 사용하면 안됨.
    
    c = calc()
    
    c(1)
    # 8
    c(2)
    # 19
    c(5)
    # 39
    

    ## 이전 식에서는 클로저의 지역변수를 가져오기만 하였다면, nonlocal을 통해 클로저의 지역 변수를 변경할 수 있다.

    ## 클로저(여기서는 calc)의 지역 변수 total = 0을 할당하였다.

    ## nonlocal total을 통해 바깥쪽 함수의 지역 변수 total을 사용하겠다는 의미로

    ## total = total + a*x +b로 변경이 가능하다.

    ## 다음은 a * x + b의 결과를 함수 calc의 지역 변수 total에 누적한다.

    ## total = total + a*x + b 에서 오른쪽의 total을 삭제하면 값이 누적되지 않는다.

    # 클로저는 다소 어려운 개념이므로 지금 당장 완벽하게 이해하지 않아도, 나중에 파이썬에 익숙해지면 자연스럽게 익히게 된다고 한다.





     

     

    728x90
Designed by Tistory.