[Python]변수의 Scope와 Namespace

Jay·2023년 1월 2일
0
post-thumbnail

Scope와 Namespace

파이썬에서 변수의 유효 범위를 계산할 때 네임스페이스(namespace)를 기반으로 계산한다. 네임스페이스는 변수를 구분할 수 있는 범위를 의미하며, 네임스페이스에는 하나의 이름이 하나의 변수만 가리키게 된다. 파이썬의 네임스페이스는 built-in, global, enclosed, local로 나뉘어지며, 각 네임스페이스에 따라 변수의 유효범위가 달라지게 된다.

built-in
파이썬의 내장되어 있는 네임스페이스이다. 파이썬으로 작성된 코드 어디에서나 사용될 수 있다.

global
전역적으로 사용이 가능한 네임스페이스이다. 파일 단위의 모듈 안에서 사용될 수 있다.

enclosed
nested function 구조에서 내부 함수에서 외부 함수의 변수에 접근할 수 있다.

local
클래스나 함수의 네임스페이스이다. 변수가 선언된 클래스나 함수의 내부에서 사용될 수 있다.


LEGB 규칙

변수를 확인할 때 local부터 enclosed, global, built-in 순서로 네임스페이스를 확인하는 것을 의미한다. 명시적으로 다른 네임스페이스의 변수를 사용하지 않는다면 LEGB 네임스페이스 순서대로 변수를 찾게 된다.

msg = "Hello"

def read_work():
	print(msg)
    print(" World")
    
def read_exception():
	print(msg)
    msg = " World"
    print(msg)
    
read_work() 		# "Hello World"
read_exception() 	# UnboundLocalError

read_work()는 정상적으로 메세지가 출력되고, read_exception의 경우에는 UnbouncLocalError가 발생하였다. 의도한 대로라면 전역 변수인 msg를 먼저 출력하고, 변경된 msg인 " World"가 출력되어야 한다. 하지만 변수를 찾을 때 local의 네임스페이스를 먼저 탐색한다. read_exception가 실행될때, print(msg) 명령어에서 msg에 해당하는 변수를 찾기 위해 local 네임스페이스를 먼저 탐색하고, msg = " World"에서 msg라는 변수가 local 네임스페이스에서 할당되었기 전역변수를 탐색하지 않게 되는 것이다.

하지만 명시적으로 네임스페이스를 명시하여 변수를 탐색할 네임스페이스를 지정할 수 있다. 해당하는 키워드로는 global, nonlocal 키워드가 존재한다.

global
바로 앞의 예제에서 발생한 문제를 해결하기 위한 키워드이다. global이라는 키워드를 붙여 해당 변수를 global 네임스페이스에서 탐색하도록 지정하는 것이다. 이렇게 정의된 변수는 = 연산자로 값을 할당하여도 local 변수로 선언되지 않고 전역 변수의 값이 변경된다.

msg = "Hello"

def write():
	global msg
    msg += " World"
    print(msg) 				# Hello World 출력
    msg = "Write new msg" 	# local 변수 msg가 아닌 전역변수 msg의 값이 변경된다.

nonlocal
nonlocal은 중첩된 함수(nested function)에서 local 네임스페이스가 아닌 enclosed 네임스페이스에서 변수를 탐색하도록 지정하는 키워드이다. 비록 중첩된 함수이더라도 네임스페이스는 local 네임스페이스를 따로 가지게 된다.


def outer():
	msg = "Hello"
    outer_var = " from outer"
    
    def inner():
        inner_var = " from inner"
        
        # print(msg, inner_var)			#UnboundLocalError
        nonlocal msg
		msg += inner_var
	
    print(msg, outer_var)				# Hello from outer
    inner()
    print(msg)							# Hello from inner

중첩함수의 내부함수에서 외부함수의 변수인 msg를 사용하게 되면 UnboundLocalError오류가 발생하게 된다. 해당하는 변수를 local 네임스페이스에서 찾을 수 없는 것이다. 전역변수를 함수 내에서 사용한 것처럼 msg를 지역변수로 인식하였다.

nonlocal 키워드는 변수를 enclosed 네임스페이스에서 찾도록 지정하는 키워드이다. inner() 함수에서 보았을 때 msg는 전역변수도 아니고 지역변수도 아니다. 이처럼 외부함수 지역변수의 네임스페이스를 탐색하기 위해서는 nonlocal 키워드를 사용하여 탐색영역을 지정할 수 있다. 또한 global 키워드로 전역변수를 가져와 사용/수정할 수 있는 것과 마찬가지로 nonlocal 키워드를 통해 외부함수의 지역변수를 사용/수정할 수 있다.

함수가 중첩된 코드에서 local변수도, 전역변수도 아닌 변수를 free variable이라고 하며, nonlocal 키워드를 통해 사용할 수 있다.


Variable Shadowing

지금까지 코드 안에서 선언/사용된 변수가 사용되는 범위에 대해서 공부하였다. 이처럼 LEGB의 네임스페이스 순서대로 변수가 탐색되며 발생하는 문제가 있다. variable shadowing이라는 문제이다. 서로 다른 네임스페이스에 선언된 같은 이름의 변수가 있을 경우, 다른 네임스페이스의 같은 변수명으로 인해 변수가 가려지는 현상이다.

var = "Global variable"

def outer():
	var = "outer local varialbe"
    
    def inner():
    	var = "inner local variable"
        print(var)
    
    inner()
    print(var)

outer()				# inner local variable \n outer local variable
print(var)			# Global variable

LEGB의 순서대로 변수를 탐색하기 때문에 같은 이름의 var 변수가 사용될 때마다 각기 다른 네임스페이스에서 변수를 탐색하여 사용되었다. 이처럼 같은 이름의 변수를 의도한대로 사용하기 위해서는 앞에서 설명한 global, nonlocal 키워드를 사용하여 변수를 탐색할 네임스페이스를 지정하여 사용해야 한다.

0개의 댓글