네임스페이스와 스코프 개념
네임스페이스란 무엇인가
변수를 선언할 때 내부적으로 어떤 일이 일어나는지 생각해보자.
username = 'leethon'
위 코드에서 할당 연산자의 우측에서는 문자열 'leethon'이라는 데이터 값이 생성되고, 메모리의 특정 영역에 저장된다. 이 데이터의 위치 정보는 변수명 username에 기록되어 관리된다.
그렇다면 변수명과 데이터 위치 정보는 어디에 저장될까? 바로 메모리 내의 특별한 공간인 네임스페이스에 저장된다. 네임스페이스는 변수명뿐만 아니라 함수명, 클래스명 등 모든 식별자를 저장하는 구조이다.
세 가지 주요 네임스페이스
- 빌트인 네임스페이스: 파이썬 인터프리터 수준에서 제공되는 이름들이 저장됨 (예: print, len, def 등)
- 글로벌 네임스페이스: 파이썬 파일 실행 시 생성되며, 해당 파일 내에서 정의된 변수, 함수, 클래스 등의 이름을 저장
- 로컬 네임스페이스: 함수 호출 시 생성되며, 해당 함수 내부에서 정의된 이름들을 저장
이 세 네임스페이스는 각각 독립된 메모리 공간으로 존재하며, 높은 수준의 네임스페이스일수록 더 넓은 범위에서 접근 가능하다.
생존 주기와 유효 범위
- 생존 주기:
- 빌트인: 파이썬 인터프리터 시작 시 생성, 종료 시 소멸
- 글로벌: 스크립트 실행 시 생성, 실행 완료 시 소멸
- 로컬: 함수 실행 시 생성, 함수 종료 시 소멸
- 유효 범위:
- 빌트인: 모든 파이썬 프로그램에서 사용 가능
- 글로벌: 현재 실행 중인 파일 내에서 정의 이후 사용 가능
- 로컬: 해당 함수 내부에서만 유효
식별자 검색 순서
동일한 이름이 여러 네임스페이스에 존재할 경우, 파이썬은 다음과 같은 우선순위로 이름을 검색한다:
- 로컬 스코프에서 사용 시: 로컬 → 글로벌 → 빌트인
- 글로벌 스코프에서 사용 시: 글로벌 → 빌트인
이 규칙 덕분에 함수 내부에서 정의한 변수가 글로벌 변수와 동일한 이름을 가져도 서로 간섭하지 않는다.
# 예제 1: 독립적인 로컬 네임스페이스
def function_a():
data = 'jason'
print(value)
def function_b():
value = 18
print(data)
# 예제 2: 중첩 함수에서의 이름 검색
counter = 1
def outer_func():
counter = 2
def inner_func():
# counter = 3
print(counter)
inner_func()
outer_func()
위 예제에서 counter 변수는 inner_func의 로컬 → outer_func의 로컬 순으로 찾아지며, print 함수는 빌트인 네임스페이스까지 탐색하여 찾게 된다.
global과 nonlocal 키워드
이 키워드들을 사용하면 기본적인 스코프 규칙을 우회하여 상위 네임스페이스의 값을 수정할 수 있다.
global 키워드 활용
score = 100
def update_score():
global score # 전역 변수 score를 참조하도록 선언
score = 666 # 새로운 지역 변수가 아닌 전역 변수 수정
print(score) # 100
update_score()
print(score) # 666 - 전역 변수 값 변경됨
nonlocal 키워드 활용
total = 100
def parent_function():
total = 200
def child_function():
nonlocal total # 상위 함수의 total 변수 참조
total = 300 # 상위 함수 변수 값 수정
print(total) # 200
child_function()
print(total) # 300 - 상위 함수 변수 변경됨
parent_function()
print(total) # 100 - 전역 변수는 영향 받지 않음
실습 문제
다음 코드들의 출력 결과를 예측하고 이유를 설명하라. global과 nonlocal을 사용해 결과를 변경해보자.
# 실습 1
balance = 100
def transaction():
balance = 666
transaction()
print(balance)
"""
출력: 100
이유: transaction 함수 내의 balance는 새로운 지역 변수로 생성되어 전역 balance에 영향을 주지 않음.
전역 변수 수정을 원한다면 함수 내에서 global balance 선언 필요.
"""
# 실습 2
balance = 100
def process_one():
balance = 666
def process_two():
balance = 888
process_two()
print(balance)
process_one()
"""
출력: 666
이유: process_two 내의 balance는 새로운 지역 변수로 생성되어 상위 함수의 balance에 영향 없음.
상위 함수 변수 수정을 원한다면 process_two 내에서 nonlocal balance 선언 필요.
"""