Python 變數作用域(Python Variable Scope)
這篇 Python 作用域教學將回答一個常見疑問:「為什麼函式裡面改了變數,外面卻沒變?」變數不是在哪裡都能存取的,它有「生效範圍」,這就是作用域(Scope)。搞懂這個,很多莫名其妙的 bug 會瞬間清楚。
區域變數與全域變數
| 種類 | 定義位置 | 生效範圍 |
|---|---|---|
| 區域變數(local) | 函式內 | 只在該函式內 |
| 全域變數(global) | 函式外(模組層級) | 整個檔案 |
message = "我是全域變數"
def show():
inner = "我是區域變數"
print(message) # 函式內可以「讀」全域變數
print(inner)
show()
print(message)
# print(inner) # 這行會報 NameError:區域變數外面看不到
執行輸出:
我是全域變數
我是區域變數
我是全域變數
函式內指派 = 建立新的區域變數
關鍵規則:在函式內對變數指派,Python 就視它為區域變數,跟外面的同名變數無關:
count = 0
def add_one():
count = 1 # 這是「新的區域變數」,不是修改全域的 count
print("函式內:", count)
add_one()
print("函式外:", count) # 全域的 count 沒變
執行輸出:
函式內: 1
函式外: 0
global:真的要改全域變數時
count = 0
def add_one():
global count # 宣告:我要用的是全域的 count
count = count + 1
add_one()
add_one()
print(count)
執行輸出:
2
能用就少用——到處 global 的程式很難追蹤誰改了什麼。比較好的做法是用參數傳入、用 return 傳出:
def add_one(count):
return count + 1
count = 0
count = add_one(count)
count = add_one(count)
print(count)
執行輸出:
2
LEGB 查找規則與 nonlocal
Python 找變數的順序是 LEGB:Local(函式內)→ Enclosing(外層函式)→ Global(模組)→ Built-in(內建)。巢狀函式要修改「外層函式」的變數時,用 nonlocal:
def counter():
n = 0
def add():
nonlocal n # 指向外層函式的 n,不是全域
n = n + 1
return n
return add
c = counter()
print(c())
print(c())
執行輸出:
1
2
常見錯誤
1. UnboundLocalError: cannot access local variable ‘x’
函式內「先讀後指派」同一個變數:因為函式內有指派,整個函式裡它都被當成區域變數,但讀取時還沒建立。要改全域就加 global,否則改用參數傳入。
x = 10
def f():
print(x) # UnboundLocalError!因為下一行有指派
x = 20
2. 蓋掉內建名稱
把變數取名為 list、sum、max 等內建函式名,之後呼叫 list() 就會報 TypeError: 'list' object is not callable。避開內建名稱(LEGB 的 B 被你蓋掉了)。
3. 以為函式能改外部的數字或字串
傳進函式的 int、str 等不可變型別,在函式內重新指派不會影響外部,要用 return 把新值傳回來。
總結
函式內指派就是區域變數;讀全域可以、改全域要 global(但盡量用參數與 return 代替);查找順序 LEGB;巢狀函式改外層用 nonlocal。理解「先讀後指派」就能秒懂 UnboundLocalError。下一篇是讓 sort 與 max 變聰明的 lambda 匿名函式。