바쁜 분은 이것만
- 함수 안에서 대입(
=,+=)하는 순간 그 이름은 함수 전체에서 지역 변수가 된다 - 모듈 맨 위 변수를 바꾸려면 함수 첫 줄에
global count, 바깥 함수 변수면nonlocal n - 더 좋은 방법은 값을 인자로 받고
return으로 돌려주기
함수 밖에 만든 변수를 함수 안에서 1 올리려고 했을 뿐인데 이런 에러가 납니다. Python 3.14에서 실행한 그대로예요.
count = 0
def add():
count += 1
return count
print(add())
Traceback (most recent call last):
File "~/work/u1.py", line 7, in <module>
print(add())
~~~^^
File "~/work/u1.py", line 4, in add
count += 1
^^^^^
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value
같은 코드를 맥에 기본으로 깔린 Python 3.9.6으로 돌리면 메시지만 다르게 나옵니다. 검색할 때 둘 다 같은 에러라고 보면 돼요.
UnboundLocalError: local variable 'count' referenced before assignment
1. 왜 나는가: 대입하면 지역 변수가 된다
파이썬은 함수를 실행하기 전에 함수 본문을 훑어서, 함수 안에서 대입하는 이름은 모두 지역 변수로 정해 둡니다. count += 1은 count = count + 1이라서 대입이에요. 그래서 함수 안의 count는 바깥 count가 아니라 아직 값이 없는 지역 변수가 되고, 오른쪽의 count + 1을 계산하려는 순간 "값이 연결되지 않은 지역 변수"라는 에러가 납니다.
헷갈리는 건 대입이 읽는 줄보다 아래에 있어도 똑같다는 점입니다.
x = 10
def show():
print(x) # 바깥 x를 읽고 싶었는데
x = 20 # 아래에서 대입하는 순간 x는 지역 변수가 됨
show()
File "~/work/u2.py", line 4, in show
print(x) # 바깥 x를 읽고 싶었는데
^
UnboundLocalError: cannot access local variable 'x' where it is not associated with a value
4번째 줄의 print(x)에서 에러가 났지만 원인은 5번째 줄의 x = 20입니다. 이 줄을 지우고 실행하면 바깥 x인 10이 그대로 출력됐어요. 에러가 난 줄만 보지 말고 같은 함수 안에 그 이름에 대입하는 줄이 있는지 찾아보세요.
2. 해결 ① global: 모듈 맨 위 변수를 바꿀 때
count = 0
def add():
global count
count += 1
return count
print(add(), add(), add())
print("count =", count)
1 2 3
count = 3
global count는 "이 함수 안의 count는 모듈 맨 위의 count"라고 알려 주는 선언입니다. 함수 첫머리에 적어 두는 게 보통이에요.
3. 해결 ② nonlocal: 함수 안의 함수에서 바깥 함수 변수를 바꿀 때
함수 안에 함수를 만든 경우(클로저)에도 같은 에러가 납니다.
def make_counter():
n = 0
def inc():
n += 1
return n
return inc
c = make_counter()
c()
File "~/work/u5.py", line 4, in inc
n += 1
^
UnboundLocalError: cannot access local variable 'n' where it is not associated with a value
여기서 n은 모듈 맨 위가 아니라 make_counter 안에 있으니 global이 아니라 nonlocal을 씁니다.
def make_counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
c = make_counter()
print(c(), c(), c())
d = make_counter()
print(d())
1 2 3
1
make_counter()를 새로 부르면 n도 새로 생겨서, d는 다시 1부터 셉니다.
반대로 바깥 함수에 없는 이름에 nonlocal을 쓰면 실행 전에 바로 막힙니다.
x = 1
def f():
nonlocal x
File "~/work/u9.py", line 3
nonlocal x
^^^^^^^^^^
SyntaxError: no binding for nonlocal 'x' found
모듈 맨 위 변수에는 global, 함수 안 변수에는 nonlocal입니다.
4. 리스트 append는 왜 global 없이 되나
items = []
def add_item(x):
items.append(x) # 대입이 아니라 메서드 호출 → global 필요 없음
add_item("a"); add_item("b")
print(items)
def reset():
items = [] # 대입 → 지역 변수 새로 생김, 바깥 items는 그대로
reset()
print(items)
['a', 'b']
['a', 'b']
items.append(x)는 이름 items에 새 값을 대입하는 게 아니라, 바깥 리스트를 꺼내서 그 안을 바꾸는 것이라 에러가 나지 않아요. 반면 reset()의 items = []는 대입이라 함수 안에 새 지역 리스트가 생기고 끝납니다. 에러도 안 나고 바깥 리스트도 안 비워져서, 이쪽이 오히려 찾기 어려운 버그가 돼요. 비우고 싶다면 items.clear()를 쓰면 됩니다.
5. 같은 에러, 다른 원인: if 안에서만 대입한 경우
def check(flag):
if flag:
result = "ok"
return result
print(check(True))
print(check(False))
ok
Traceback (most recent call last):
...
File "~/work/u8.py", line 4, in check
return result
^^^^^^
UnboundLocalError: cannot access local variable 'result' where it is not associated with a value
바깥 변수와는 상관없는 경우입니다. flag가 False면 result = "ok" 줄을 건너뛰어서 값이 없는 채로 return에 도착해요. if 위에 result = None처럼 기본값을 먼저 넣어 두면 됩니다. try 안에서만 변수를 만들고 except 뒤에서 쓰는 경우도 같은 이유로 납니다.
def load():
try:
data = int("abc")
except ValueError:
print("변환 실패")
return data
load()
변환 실패
...
UnboundLocalError: cannot access local variable 'data' where it is not associated with a value
int("abc")가 실패해서 data에 값이 들어가지 못했어요. except 안에서 data = 0처럼 대신할 값을 넣거나, 그냥 return하면 됩니다.
6. global을 안 쓰는 방법
global은 짧은 스크립트에선 편하지만, 함수가 늘어나면 어디서 값이 바뀌는지 따라가기 어렵습니다. 값을 인자로 받고 돌려주거나, 상태가 여러 개면 클래스로 묶는 방법도 있어요.
def add(count):
return count + 1
count = 0
count = add(count)
count = add(count)
print("count =", count)
class Counter:
def __init__(self):
self.n = 0
def add(self):
self.n += 1
return self.n
c = Counter()
c.add(); c.add()
print("c.n =", c.n)
count = 2
c.n = 2
self.n += 1은 이름 n이 아니라 객체의 속성을 바꾸는 것이라 global·nonlocal이 필요 없습니다.
정리
| 상황 | 해결 |
|---|---|
함수 안에서 모듈 맨 위 변수에 += |
global 이름 또는 인자·return |
안쪽 함수에서 바깥 함수 변수에 += |
nonlocal 이름 |
| 읽는 줄 아래에 같은 이름 대입이 있음 | 변수 이름을 다르게 |
| if·try 안에서만 대입 | 위에서 기본값 먼저 넣기 |
| 리스트·딕셔너리 내용만 바꿈 | 에러 안 남 (append, clear, d[k] = v) |
참고: Python 공식 문서 — Programming FAQ "Why am I getting an UnboundLocalError when the variable has a value?", Language Reference 7.12 The global statement, 7.13 The nonlocal statement
