Nonlocal statement에 대한 심화설명.PEP와 Python Docs의 내용을 참조해서 알아보자
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을 제시해줍니다.
- Syntax Error를 발생시킨다. (Compile Error가 아닌)
- 적절한 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
Leave a comment