2 minute read

23.12.13 Reference 추가

23.12.24 Why nonlocal syntax error 추가 global declaration으로 global scope에 있는 name을 reference 뿐만 아니라 , assignment를 하기도 합니다. enclosing function scope에서 reference를 하는 방법을 배웠지만 , assignment를 하는 방법은 아직 소개하지 않았씁니다.

Nonlocal

python 3.X 버전 부터는 nonlocal이라는 namespace declartion 을 사용할 수 있습니다. 이 declartion statement 는 global declartion과 매우 유사합니다. 이제부터 , 이에 대해서 알아보도록 하겠습니다.

Assignment

X = 88

def C() :
    global X
    X = 999

    print(X)
C()
print(X)

global statement를 사용하면 global scope에 assignment가 가능해집니다.

def tester(start) :
    state = start
    def nested(label) :
        nonlocal state
        print(label , state)
        state +=1 
        
    return nested

F = tester(0)

F('spam')

F('ham')

nonlocal statement를 사용하게 되면 , 마찬 가지로 enclosing func scope에 있는 name에 assignment를 할 수 있습니다 .

Skip search scope

global statement는 enclosing module scope부터 name이 scope에 존재하는지 search를 시작하게 됩니다.global scope다음에는 , built-in scope를 찾아보고 , scope에 name이 존재하지 않는다면, global scope에 name을 만듭니다. 그리고 , assignment를 하게 됩니다.

nonlocal statement도 이와 유사하게 enclosing func scope에서 이름을 검색하기 시작합니다. 하지만 global과 달리 , enclosing func scope에 name이 존재하지 않는다면 , Syntax error가 발생합니다.

scope 제한

global statement는 어떤 scope에서도 사용이 가능합니다. 하지만, nonlocal statement은 enclosing function scope에서만 사용이 가능합니다.

pre assigned

global statement는 위에서 말 했듯이 , scope에 name이 존재 하지 않는다면 , name을 만든다고 하였습니다. 하지만, nonlocal statement는 enclosing scope에 name이 존재하지 않는다면 , Syntaxerror가 납니다. 즉, enclosing scope에 name이 없다면 새로 만들지 못하기에 , name이 존재해야만 합니다.이는 serach 범위의 우선순위에 의한 차이때문에 어쩔 수 없는 설계입니다. Python에서 name을 search할려면 LEGB 순으로 name resolution을 진행하게 되는데 , nonlocal에 assign하게 되면 Enclosing func scope와 Global scope중 어떤 scope에 속하는지 name만으로 알수 가 없게 됩니다. 따라서, nonlocal에서는 assign을 허용하지 않습니다.

Why Syntax Error?

Nonlocal statement도 global statement 처럼 parent function의 frame에 name을 만들어서 assign을 하면 되지 않을까라는 의문이 생길 수 있습니다. 이에 대한 답변으로 Python 창시자인 Guido van Rossum 의 설명을 그대로 첨부하도록 하겠습니다.

  def outer():
    def inner1(x):
      global a
      a = x
    def inner2():
      return a
    return inner1, inner2
  f1, f2 = outer()
  g1, g2 = outer()
  f1(42)
  g1(0)
  print(f2())

위 파이썬의 예제의 경우 f2() 는 0을 출력합니다. f1(42)를 호출하는 과정에서 global frame에 a를 만들었고 마지막에 assign한 시점이 g1(0)를 호출하면서 입니다. 따라서 , a에는 0이 최종적으로 assign되고 0을 출력하게 됩니다.

만약에, global 대신 nonlocal 이였다면 어떻게 동작을 했어야 했을까요?
이에 대해서, 2가지 solution을 제시해줍니다.

  1. Syntax Error를 발생시킨다. (Compile Error가 아닌)
  2. 적절한 scope를 선택해서 name을 생성한다.

만약에 2를 선택한다면, 아래와 같이 동작을 시킬 수 있습니다. outer scope가 1개라면 global frame에 name을 생성하면 됩니다. outer scope가 여러개가 있다면 outer scope중 innermost scope에 name을 생성하면됩니다.

여기까지는 자연스럽지만, 만약에 outer scope가 없다면, outer scope에 name을 생성하는 방법을 적용할 수 없습니다. 예외를 두어야만 하는데, 이는 자연스럽지 않습니다.

따라서, Guido van Rossum는 syntax error를 발생시켜야만 한다고 주장하고, syntax error를 발생시키도록 python을 수정하였습니다.

lifecycle

global statement를 사용하게 되면 , global scope에 name이 bind되기 때문에 , global scope를 벗어나기 전까지 , name(variable)이 살아있어서 access할 수 있습니다. 하지만, nonlocal statement 로 assign한 name은 enclosing scope에 있기 때문에 그 scope를 벗어난다면 name의 수명이 끝나게 됩니다.(물론, closure function을 사용해서 name을 enclosing block에 bind할 수 있긴합니다.)

Nonlocal의 장점

그렇다면, Nonlocal statement를 사용하는 이유는 무엇일까요? Nonlocal statement의 의의는 변경 가능한 state들의 copy를 메모리에 보존한다 에 그 의의가 있습니다. 따라서, 아래와 같은 상황에 유효하다고 볼 수 있습니다.

  • global variable을 사용할 수 없을 때
  • Class를 사용하는 것이 overkilled solution일 때

Reference

PEP 3104 – Access to Names in Outer Scopes Python.org

Learning python chp17 Scope : Learning Python chp 17

4.2.2. Resolution of names

9.2. 파이썬 스코프와 이름 공간

7.12. global 문 7.13. nonlocal 문

Nonlocal Syntax Error reason

Leave a comment