파이썬은 이름을 찾을 때 Local → Enclosing → Global → Built-in 순서로 스코프를 뒤진다. 앞 글자를 따 LEGB(Local, Enclosing, Global, Built-in) 규칙이라 부른다. 함수 안에서 만든 변수는 그 함수 안에서만 살고, 바깥 함수의 변수는 안쪽 함수가 읽을 수 있다.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
print(x) # enclosing
print(len) # built-in- Local: 현재 함수 안
- Enclosing: 감싸고 있는 바깥 함수들
- Global: 모듈 최상위
- Built-in:
len,print같은 내장 이름
대입하면 지역 변수가 된다
함수 안 어디서든 이름에 대입하면 그 이름은 함수 전체에서 지역 변수로 취급된다. 그래서 대입 전에 읽으면 바깥 값이 아니라 UnboundLocalError가 난다.
count = 0
def inc():
global count # 모듈 변수를 바꾸겠다고 선언
count += 1
def make_counter():
n = 0
def step():
nonlocal n # 바깥 함수 변수를 바꾸겠다고 선언
n += 1
return n
return step
c = make_counter(); c(); c() # 2global은 모듈 변수,nonlocal은 가장 가까운 바깥 함수의 변수를 가리킨다- 읽기만 할 때는 선언이 필요 없다. 리스트에
append하는 것도 대입이 아니라서 선언 없이 된다
클로저
make_counter가 끝난 뒤에도 step은 n을 기억한다. 바깥 함수의 변수를 붙잡은 안쪽 함수를 클로저(closure)라 하고, 자바스크립트의 클로저와 같은 개념이다. 함수가 일급 객체(first-class object)라 변수에 담고, 인자로 넘기고, 반환할 수 있기 때문에 가능하며, 데코레이터가 이 위에 서 있다.
주의
- 반복문 안에서 만든 람다가 반복 변수를 붙잡으면, 호출 시점의 마지막 값을 본다(늦은 바인딩, late binding). 기본 인자로 값을 고정한다:
lambda i=i: i - 전역 변수로 함수끼리 데이터를 주고받으면 흐름을 추적하기 어렵다. 인자와 반환값으로 넘긴다(변수·컬렉션 캡슐화)
- 내장 이름(
list,id,type)을 변수명으로 덮어쓰지 않는다
스코프 규칙과 함께 함수 인자 처리는 *args·**kwargs와 언패킹, 모듈 단위의 이름 공간(namespace)은 파이썬 모듈과 패키지를 본다.
출처: Python 튜토리얼: Scopes and Namespaces · 실행 모델: Resolution of names