Python 閉包遵循 LEGB (Local, Enclosing, Global, Built-in) 變數查找規則。巢狀函數對外層變數「可讀不可寫」是常見陷阱。本文透過實例剖析 UnboundLocalError 成因,提供 Python 2/3 多種解決方案。
def foo():
a = 1
def bar():
a = a + 1 # UnboundLocalError!
bar()
錯誤原因:a = ... 使 Python 編譯器判定 a 為區域變數,但右側 a + 1 卻在賦值前讀取,導致「引用前未定義」。
| 程式碼 | 結果 | 說明 |
|---|---|---|
print a + 1 | ✅ 正常 | 僅讀取,查找到 Enclosing 的 a |
b = a + 1 | ✅ 正常 | 建立區域 b,讀取 Enclosing a |
a = 1 | ✅ 正常 | 建立**新的區域 a**,遮蔽外層 |
a = a + 1 | ❌ UnboundLocalError | 視為區域變數,但右側讀取未定義 |
def foo():
a = 1
def bar():
b = a + 1 # 僅讀取
print b
bar()
def foo():
a = [1] # 列表可變
def bar():
a[0] = a[0] + 1 # 修改容器內容,非重新綁定變數
bar()
print a[0] # 2
def foo():
a = 1
def bar():
nonlocal a
a = a + 1 # 明確聲明修改 Enclosing 變數
bar()
print a # 2
flist = []
for i in xrange(3):
def func(x):
return x * i
flist.append(func)
for f in flist:
print f(2)
# 預期:0, 2, 4
# 實際:4, 4, 4 (所有函數捕捉同一個 i,最終值為 2)
flist = []
for i in range(3):
def func(x, i=i): # 預設參數捕捉當前 i
return x * i
flist.append(func)
def make_func(i):
def func(x):
return x * i
return func
flist = [make_func(i) for i in xrange(3)]
nonlocal 明確語意nonlocal