• 파이썬 변수의 범위

    2021. 5. 22.

    by. Jacob Lee

    728x90

     

    범위란 변수가 정의되고 검색될 수 있는 장소를 의미한다.


    파이썬 범위

    파이썬은 이름이 거주하는 장소인 네임스페이스에 이름을 생성, 변경, 검색한다.

    코드와 관련하여 이름의 값을 찾고자 할 때 범위라는 용어는 '네임스페이스'를 의미한다.

    즉, 소스 코드에서 이름이 할당되는 위치는 코드에서 이름이 보이는 범위를 결정한다.

    기본적으로 함수 내에 할당된 모든 이름은 해당 함수의 네임스페이스에 연결되어 있지, 다른 곳과 연결되지는 않는다.

    • def 내에 할당된 이름들은 오직 그 def 내의 코드에 의해서만 보인다. 그 함수 외부에서는 그런 이름이 있는지 확인조차 할 수 없다.
    • def 내에 할당된 이름들은 비록 동일한 이름이 다른 곳에서 사용되고 있더라도 def 바깥의 변수들과 충돌하지 않는다. 주어진 def문 밖에서 할당된(즉, 다른 def문 또는 모듈 파일의 최상위 레벨에서 할당된) 이름 X는 그 def문 안에 할당된 이름 X와는 전혀 다르다.

     

    모든 경우에 변수의 범위(변수가 사용될 수 있는 범위)는 항상 해당 변수가 소스 코드 내 어디에서 할당되었느냐로 결정되며, 어떤 함수가 무엇을 호출했는지와는 무관하다.

     

    변수는 세 개의 서로 다른 범위에 해당하는 세 개의 다른 위치에서 할당될 수 있다.

    • 변수가 def문 안에 할당되면 해당 함수에 대하여 지역(local) 범위를 갖는다.
    • 변수가 바깥쪽 def 안에서 할당되면 이는 중첩된 함수에 대한 비지역(nonlocal) 변수다.
    • 변수가 모든 def의 바깥에서 할당되면 이는 전체 파일의 대하 전역(global) 변수다.

    변수의 범위는 함수 호출이 아닌 전적으로 프로그램 파일의 소스 코드 안에서 변수의 위치에 따라 결정되므로 우리는 이를 어휘 범위(lexical scoping)라고 부른다.

     

    아래 예제에서 전역 변수 X와 def 안의 지역 변수 X를 각각 생성하는 것을 확인할 수 있다.

    X = 99        # 전역 범위 (모듈 범위) X
    
    def func():
        X = 88    # 지역 범위 (함수 범위) X

    위처럼 두 개의 변수가 같은 이름을 가질지라도, 갖는 범위가 서로를 다르게 인식하게 한다. 함수의 범위가 프로그램에서 이름 충돌을 피하도록 돕고, 함수가 보다 자립된 프로그램 단위로 구성되도록 한다.

     


     

     

    범위의 세부 사항

    • 모듈은 전역 범위다. 각 모듈은 전역 범위다. 이는 변수가 모듈 파일의 최상위 레벨에서 생성(할당)되는 변수가 거주하는 네임스페이스이다.
    • 전역 범위는 단일 파일에만 해당된다. '전역'이라는 단어의 뜻이 모든 파일의 전역이란 뜻은 아니다, 단지 단일 파일 내의 코드에 대하여 전역 범위를 갖는 것이다. 이름은 모듈에 따라 나뉘기 때문에, 다른 파일에서 정의한 이름을 사용하기 원한다면 해당 모듈을 항상 명시적으로 임포트 해야 한다. 즉, 파이썬에서 '전역'이라는 말은 '모듈'로 이해할 수 있다.
    • 할당된 이름은 전역이나 비지역으로 선언하지 않는 이상 지역이다. 기본적으로, 함수 정의 문안에 할당된 모든 이름은 지역 범위(함수 호출과 연계된 네임스페이스)에 놓이게 된다. 만약 함수를 둘러싼 모듈의 최상위 레벨에 존재하는 이름을 할당할 필요가 있다면, 함수 내에서 global문을 사용하고, 바깥쪽 def에 존재하는 이름을 할당하고 싶다면, nonlocal문 안에 선언하면 된다.
    • 지역 외 다른 이름은 바깥쪽 함수의 지역, 전역, 내장된 이름이다. 함수 정의 안에서 값이 할당되지 않은 이름은 바깥쪽(enclosing) 범위의 지역이거나, 모듈 네임스페이스에 거주하는 전역(global)이거나, 또는 파이썬에서 제공하는 미리 정의된 내장 모듈의 내장(built-ins) 된 이름이다.
    • 각각의 함수 호출은 새로운 지역 범위를 만든다. 함수가 호출될 때마다 해당 함수 내에서 생성된 이름이 일반적으로 거주하는 네임스페이스, 즉 지역 범위가 새로 생성된다. def문이나 lambda가 새로운 지역 범위를 정의한다고 생각할 수 있지만, 지역 범위는 실제로 함수 호출에 상응하여 정의된다.

     


     

    파이썬의 이름 확인 방식

    앞서 배운 내용을 간단하게 세 가지 원칙으로 설명하면:

    • def문 내에서 이름 할당은 기본적으로 지역 이름을 생성하거나 변경한다.
    • def문 내에서 이름 참조는 지역 범위, 바깥쪽 함수 범위(만약 있다면), 전역 범위, 그리고 내장된 범위와 같이 최대 네 가지의 범위를 탐색한다.
    • def문 내에서 global 또는 nonlocal문에서 선언된 이름은 각각 바깥쪽 모듈과 바깥쪽 함수의 범위에서 할당된 이름에 연결된다.

     

    즉, def문이나 lambda 내에 할당된 모든 이름은 기본적으로 지역 범위를 갖는다.

     

    파이썬의 이름 확인 방식은 범위 이름의 앞자를 따서 LEGB 규칙이라고도 불린다.

    • 함수 내에서 검증되지 않은 이름을 사용하면, 파이썬은 지역 범위(local, L), 여기에 없으면 바깥쪽(enclosing, E) def나 lambda의 지역 범위, 여기에도 없다면 전역(global, G) 범위, 그리고 마지막으로 내장된(built-in, B) 범위에서 이름을 찾는다. 이름이 처음 발견된 위치에서 이름 찾기를 중단하고, 이름이 발견되지 않는다면 오류를 보고한다.
    • 함수 안에서 이름을 할당할 때, 파이썬은 항상 지역 범위에 이름을 생성하거나 변경한다.
    • 만약 이름이 어떠한 함수에도 포함되지 않는 외부에서 할당된다면(즉, 모듈 파일의 최상위 위치 또는 대화형 프롬프트), 지역 범위는 모듈 네임스페이스인 전역 범위와 동일하다.

     


     

    Global문

    global문은 함수가 하나 이상의 전역 이름(함수를 포함하고 있는 모듈 범위(네임스페이스)에 존재하는 이름)을 변경할 계획임을 파이썬에게 알려준다.

    • 전역 이름은 모듈 파일의 최상위 레벨에 할당된 변수다.
    • 전역 이름은 함수 내에서 할당될 때에만 선언되어야 한다.
    • 전역 이름은 선언되지 않더라도 함수 내에서 참조될 수 있다.

     

    즉, global은 def의 바깥 모듈 파일의 최상위에 위치하는 이름을 변경할 수 있게 한다.

     

    다음은 global 선언을 함으로써 def 안의 X가 def 바깥의 X를 의미하게 된다.

    X = 88             # 전역 X
    
    def func():
        global X
        X = 99         # 전역 X: def 바깥 범위
    
    func()
    print(X)           # 99 출력

    이처럼 global을 사용해서 두 변수를 동일하게 되었으므로, 함수 내의 X를 변경하면 함수 밖의 X도 함께 바뀌게 된다.

     

    다음 예제는 조금 더 복잡한 상황을 다룬다.

    y, z = 1, 2
    def all_global():
        global x
        x = y + z

    y, z는 함수 내에서 할당되지 않았으므로 당연히 전역 변수이지만, x는 방금 함수 내에서 정의 되었다.

    보통 x 같은 상황이라면 지역 변수로 정의되겠지만, global문으로 모듈 범위에 매핑하였으므로 전역 변수가 되었다.

    즉, x는 함수가 실행되기 전에는 모듈에 존재하지도 않았지만, 함수 내의 첫 할당으로 모듈에 x가 생성되었다.

     

     

     

    Nonlocal문

    Nonlocal문을 사용하면 변경 가능한 상태 정보를 제공하여 중첩된 def가 바깥쪽 함수의 이름에 대한 읽기, 쓰기 권한을 모두 갖게 된다.

     

    Nonlocal vs. Global

    • nonlocal과 global 둘 다 바깥쪽 범위에서 변경될 이름을 선언한다.
    • 하지만 nonlocal은 global과 달리 def 외부의 전역 모듈 범위가 아닌, 바깥쪽 함수의 범위에 있는 이름에 적용된다.
    • nonlocal 이름은 선언될 때 이미 바깥쪽 함수의 범위에 존재해야 한다.

     

    즉, nonlocal은 바깥쪽 함수 범위의 이름에 할당하고, 해당 이름의 범위 검색을 바깥쪽 def로 제한하는 두 역할을 수행한다.

    • global = 모듈의 존재하는 이름
    • nonlocal = 바깥쪽 def 내에 거주하는 이름

     

    변경을 위한 nonlocal

    아래 예제처럼 중첩 함수 안에서 바깥쪽 def에 있는 이름을 nonlocal문으로 선언하면 중첩 함수 안에서 그 변수를 변경할 수 있다.

    >>> def tester(start):
    ...     state = start
    ...     def nested(label):
    ...         nonlocal state
    ...         state = +=1
    ...         print(label, state)
    ...     return nested
    
    >>> F = tester(0)
    >>> F('spam')
    spam 0
    >>> F('ham')
    ham 1
    >>> F('eggs')
    eggs 2

     

    영역 문제

    전역 이름은 선언 전에 미리 정의되지 않아도 되지만 비지역 이름은 평가되기 전에 바깥쪽 def문에서 '반드시' 할당되어 있어야 한다.

    >>> def tester(start):
    ...     def nested(label):
    ...         nonlocal state
    ...         state = 0
    ...         print(label, state)
    ...     return nested
    
    SyntaxError: no binding for nonlocal 'state' found
    
    >>> def tester(start):
    ...     def nested(label):
    ...         global state
    ...         state = 0
    ...         print(label, state)
    ...     return nested
    
    >>> F = tester(0)
    >>> F('abc')
    abc 0
    >>> state
    0

     

    또한, 비지역은 범위 검색을 바깥쪽 def로 제한한다.

    >>> spam = 99
    >>> def tester():
    ...     def nested():
    ...         nonlocal spam
    ...         print('Current=', spam)
    ...         spam += 1
    ...     return nested
    
    SyntaxError: no binding for nonlocal 'spam' found

    이런 제약이 없다면, 파이썬은 어느 바깥쪽 범위에서 새로운 이름이 생성되었는지 알 수 없다. 여러 모호한 문제를 피하기 위해 파이썬은 비지역을 함수 호출 시점이 아닌, 함수 생성 시점에 검사한다.

     


     

    Nonlocal & Global 총 정리

    • global은 범위 검색을 모듈 범위부터 시작하도록 하고, 전역 범위에 있는 이름을 할당한다.
      범위 검색은 만약 모듈 범위에 해당 이름이 존재하지 않는다면 내장 범위까지 계속된다.
      하지만 전역 이름에 대한 할당은 항상 모듈 범위에 해당 이름을 생성하고 변경한다.
    • nonlocal은 범위 검색을 오직 바깥쪽 def 내로 제한하고 그 이름들은 이미 바깥쪽 def에 존재하고 있어야 하며,
      그 이름들이 할당받을 수 있도록 한다. 범위 검색은 전역 범위나 내장 범위로 확장 진행되지 않는다.

     

     

    Reference

    728x90

    'Back-end > Python' 카테고리의 다른 글

    파이썬 이터레이터  (0) 2021.07.09
    파이썬으로 csv 파일 읽기  (0) 2021.06.26

    댓글