Python 變數作用域:global vs nonlocal 完整圖解
你有沒有遇過這種情況:在函式裡面明明改了變數的值,結果外面的變數卻紋風不動?或者加了一個 global,結果改到了不該改的東西?
這篇文章會帶你從零開始,搞懂 Python 變數作用域的核心機制——LEGB 規則,以及 global 和 nonlocal 這兩個關鍵字到底在做什麼。文末還有互動式演示和隨堂測驗,幫你徹底內化這些觀念。
什麼是 LEGB 規則?
Python 查找變數時,會按照固定的順序搜尋四個「作用域(Scope)」,這就是所謂的 LEGB 規則:
- L — Local(區域):目前函式內部定義的變數
- E — Enclosing(外層函式):包住當前函式的外層函式中的變數(巢狀函式才有)
- G — Global(全域):模組(檔案)層級的變數
- B — Built-in(內建):Python 內建的名稱,例如
print、len
Python 會從 L 開始,一路往外找到 B。找到的第一個就用,找不到就報 NameError。
x = "全域" # G — Global
def outer():
x = "外層" # E — Enclosing
def inner():
x = "區域" # L — Local
print(x) # 先找 L → 找到了!印出 "區域"
inner()
outer()賦值的陷阱:為什麼改不到外面的變數?
Python 有一條重要的規則:在函式內部對變數賦值,Python 會自動將它視為區域變數,不管外面有沒有同名的變數。
count = 0
def increment():
count = count + 1 # ❌ UnboundLocalError!
# Python 看到 count = ... 就認定 count 是區域變數
# 但右邊的 count 還沒被賦值,所以報錯
increment()這就是為什麼我們需要 global 和 nonlocal 來「宣告」我們要修改的是哪一層的變數。
global 關鍵字
global 告訴 Python:「這個變數不是區域的,請直接去全域(模組層級)找。」
count = 0
def increment():
global count # 宣告:我要用全域的 count
count = count + 1 # ✅ 現在可以修改了
increment()
increment()
print(count) # 輸出 2global 會讓程式碼難以維護和除錯。在大型專案中,盡量避免使用全域變數,改用參數傳遞或類別封裝。nonlocal關鍵字
nonlocal 告訴 Python:「這個變數不是區域的,請去上一層的外層函式找。」它只能用在巢狀函式中。
def make_counter():
count = 0 # 外層函式的變數
def increment():
nonlocal count # 宣告:我要用外層的 count
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3這是 nonlocal 最經典的應用場景——閉包(Closure)。外層函式的變數在函式結束後不會消失,而是被內層函式「記住」了。
global vs nonlocal:到底選哪個?
實戰範例:裝飾器中的 nonlocal
nonlocal 在裝飾器中非常實用。以下是一個「呼叫計數器」的範例:
def call_counter(func):
"""記錄函式被呼叫了幾次"""
count = 0
def wrapper(*args, **kwargs):
nonlocal count
count += 1
print(f"{func.__name__} 已被呼叫 {count} 次")
return func(*args, **kwargs)
return wrapper
@call_counter
def greet(name):
print(f"你好,{name}!")
greet("小明") # greet 已被呼叫 1 次 → 你好,小明!
greet("小華") # greet 已被呼叫 2 次 → 你好,小華!常見錯誤與陷阱
陷阱 1:忘記宣告就修改
total = 0
def add(n):
total += n # ❌ UnboundLocalError
# 解法:加上 global total陷阱 2:global 穿透多層函式
x = "全域"
def outer():
x = "外層"
def inner():
global x # 跳過外層,直接改全域!
x = "內層"
inner()
print(x) # 印出 "外層"(沒被改到)
outer()
print(x) # 印出 "內層"(全域被改了)如果你的意圖是修改 outer 裡的 x,應該用 nonlocal 而不是 global。
陷阱 3:nonlocal 不能用在最外層
x = 10
def my_func():
nonlocal x # ❌ SyntaxError: no binding for nonlocal 'x' found
# nonlocal 只能用在巢狀函式中,不能指向全域變數互動練習
理論看完了,現在動手試試看吧!在「學習模式」中點選不同的按鈕,觀察變數如何變化;在「挑戰模式」中測試你的理解。
互動演示:global vs nonlocal
透過視覺化互動,理解變數在不同層級中的覆寫與修改行為。
互動演示區
要在內層函式修改 x,請選擇一種方式:
原理解析
請選擇一個操作
Python 的作用域就像俄羅斯娃娃。最外層是「全域 (Global)」,中間是「外層函式 (Enclosing)」,最裡面是「內層函式 (Local)」。
# 初始狀態x = "🍎" # 全域變數def outer_func():x = "🍊" # 外層變數def inner_func():# 等待你的操作...pass
總結
- Python 用 LEGB 規則查找變數:Local → Enclosing → Global → Built-in
- 在函式中賦值會自動建立區域變數,不會修改外層
global讓你修改模組層級的變數(盡量少用)
nonlocal讓你修改外層函式的變數(閉包、裝飾器常用)
- 遇到
UnboundLocalError時,先檢查是不是忘了加global或nonlocal
global。當你真的需要「狀態記憶」時,考慮使用類別或 nonlocal 搭配閉包。版權聲明
文章標題:Python 變數作用域:global vs nonlocal 完整圖解
文章作者:阿盧老師
文章連結:https://codinglu.tw/blog/python-nonlocal-global
授權條款:本文採用 CC BY-NC 4.0 授權。轉載請標明出處。