들어가며
python으로 알고리즘 문제를 풀다 보면 외부에서 선언된 변수를 인자를 주지 않고, 함수에서 그대로 사용하는 경우가 있다.
이따금씩 global 선언을 하지 않아, 함수 내에서 UnboundLocalError가 일어나기도 한다.
지금까지는 그냥 그런가보다 하고 사용하고 있었는데, 갑자기 어떤 케이스에서 무슨 이유로 이런 에러가 발생하는지 궁금해졌다.
네임스페이스(namespace)
namespace의 정의를 살펴보면 특정한 객체(Object)를 이름(name)에 따라서 구분할 수 있는 범위를 뜻한다.
파이썬은 모두 객체로 구성되어 있고, 각각의 객체는 특정한 이름과 mapping 관계를 가지고 있다. 이런 매핑을 포함하는 공간을 namespace라고 부른다.
크게 세가지의 namespace로 구분되는데,
Local namespaceGlobal namespaceBuilt-in namespace가 존재한다.
파이썬에 내장된 기본 함수 등의 name이 Built-in namespace에 속해있고,
모듈(하나의 .py 파일로 이해할 수 있을 것 같다.) 전체에서 사용될 수 있는 이름은 Global namespace에 속한다.
마지막으로 함수 def 내의 공간에 할당 되는 변수들은 Local namespace에 속한다.
그렇다면 동일한 name에 대해 우선순위는 어떻게 구성될까?
먼저 가장 내부의 Local namespace부터 탐색, 점차 바깥의 Local namespace로 올라가며, 이후 Global namespace를 마지막으로 Built-in namespace를 검색하게 된다.
요약하면 name의 탐색 우선 순위는
Built-in << Global << Local 이 되는 것이다.
예시
다음과 같이 외부와 내부 모두에서 변수 N을 선언된 경우에는 Local이 앞서므로, N=3으로 매핑된 것을 확인할 수 있다.
확실하게 하기 위해 다음과 같이 locals()와 globals() 함수를 통해 Local namespace와 Global namespace가 각각 어떠한 값과 매핑되고 있는지 확인할 수 있다.
다음과 같이 func1 함수에서 local의 우선순위가 앞서기 때문에 N=3이 출력되는 것을 확인할 수 있다.
global 선언을 해주니 Local namespace로 설정되지 않고, 바로 Global namespace로 매핑된 것을 확인할 수 있다.
결론
도입에서 UnboundLocalError가 발생한 이유를 정리하자면 다음과 같다.
함수 내에서 할당 연산을 하므로 변수 N은 local namespace의 name으로 간주된다.
N에 N+1의 값을 할당해야 하지만, local namespace에서 N을 참조할 수 없다.
이런 경우 local namespace의 N에 값을 할당할 수 없으므로 UnboundLocalError가 발생하게 된다.