1. 前言:什麼是 Python 的變數作用域?
在學習 Python 的過程中,常常會遇到「變數的作用域(Scope)」這個概念。作用域是指變數可以被存取的範圍,如果沒有正確理解,可能會導致預期之外的錯誤或 Bug。
例如,請看看下面這段程式碼:
def my_function():
x = 10 # 這個變數 x 僅在函式內部有效
print(x) # NameError: name 'x' is not defined
這段程式碼中,試圖在函式 my_function
外部參考裡面定義的變數 x
,結果導致錯誤。這是因為x 屬於「區域作用域」(Local Scope),在函式外部就不存在。
本文將從 Python 的變數作用域基礎開始,詳細解說進階用法與錯誤排除方法,幫助你正確理解與管理作用域。
2. 變數作用域的基本概念
2.1 什麼是變數作用域?
變數作用域是指變數可以被使用的範圍。在 Python 中,變數定義的位置會決定它可以被參考的範圍。
例如,在函式內定義的變數無法在函式外部存取。相對地,在模組層級定義的變數可以在整個腳本中使用。
2.2 為什麼要理解作用域?
如果沒有正確管理變數作用域,可能會導致以下問題:
- 不小心覆蓋變數
在不同作用域中使用相同名稱的變數,可能會造成預期外的 Bug。 - 找不到變數(NameError)
如果不了解作用域規則,試圖存取未定義的變數,就會出現NameError
。 - 程式可讀性降低
過度使用全域變數會讓程式邏輯變得不清晰,也更難除錯。
2.3 Python 作用域的基本規則
為了正確理解 Python 的作用域,請掌握以下幾個要點:
- 函式內定義的變數只在該函式內有效(區域作用域)
- 函式外定義的變數可在整個腳本中使用(全域作用域)
- Python 使用「LEGB 規則」來決定變數的搜尋順序
- 若要在函式中修改全域變數,需使用
global
關鍵字 - 若要在巢狀函式中修改外層變數,需使用
nonlocal
關鍵字
透過妥善管理作用域,可以有效預防錯誤並提升程式的品質與效率。
3. Python 的作用域種類與範例
在 Python 中,變數的作用域大致可分為 四種類型。了解每一種作用域的特性並正確管理變數,是撰寫無錯誤程式碼的關鍵。
- 區域作用域(Local Scope)
- 包覆作用域(Enclosing Scope)
- 全域作用域(Global Scope)
- 內建作用域(Built-in Scope)
在本節中,我們將透過具體的範例來詳細說明這四種作用域。
3.1 區域作用域(Local Scope)
區域作用域是指在函式內部定義的變數所擁有的作用範圍。這些變數只在該函式內有效,無法在函式外部存取。
區域作用域範例
def my_function():
x = 10 # x 是此函式的區域變數
print(x) # 這行會正常執行
my_function()
print(x) # NameError: name 'x' is not defined
說明:
變數 x
僅在 my_function
執行期間有效,函式外部無法存取,否則會出現 NameError
。
3.2 包覆作用域(Enclosing Scope)
包覆作用域是指巢狀函式中外層函式的變數作用範圍。內部函式(子函式)可以存取外部函式(父函式)中定義的變數。
包覆作用域範例
def outer_function():
y = 20 # 父函式中的變數
def inner_function():
print(y) # 子函式可以存取父函式的變數
inner_function()
outer_function()
說明:inner_function
可以存取 outer_function
中的變數 y
,這就是包覆作用域的特性。
3.3 全域作用域(Global Scope)
全域作用域是指整個模組(腳本)中都可以存取的變數作用範圍。在函式外部定義的變數,任何地方都能參考。
全域作用域範例
z = 30 # 全域變數
def my_function():
print(z) # 可以存取全域變數
my_function()
print(z) # 在任何地方都可存取
3.4 內建作用域(Built-in Scope)
內建作用域是指Python 預設提供的內建函式與變數所屬的作用域。例如 print()
、len()
等函式即屬於此範圍。
內建作用域範例
print(len([1, 2, 3])) # 輸出:3
說明:print
與 len
是 Python 的內建函式,無論在哪個作用域都可以使用。
3.5 四種作用域總結
作用域名稱 | 說明 | 範例 |
---|---|---|
區域作用域 | 僅在函式內部有效的變數 | 函式內的變數 |
包覆作用域 | 可由巢狀函式存取外層函式的變數 | 巢狀函式中的變數 |
全域作用域 | 整個腳本中皆可參考的變數 | global 變數 |
內建作用域 | Python 內建函式與變數 | print() , len() |
4. 變數作用域的查找順序(LEGB 規則)
在 Python 中,當你參考某個變數時,會依照特定的順序來查找變數。這個查找順序稱為 LEGB 規則(Local、Enclosing、Global、Built-in),查找順序如下:
- L(Local)區域作用域 – 目前函式內定義的變數
- E(Enclosing)包覆作用域 – 外層(巢狀)函式中定義的變數
- G(Global)全域作用域 – 模組層級定義的變數
- B(Built-in)內建作用域 – Python 預設提供的內建函式與變數
理解這個規則可以幫助你預測變數是從哪裡被解析的,進而避免錯誤的發生。
4.1 LEGB 規則的範例
x = "全域變數" # G(Global)
def outer_function():
x = "包覆變數" # E(Enclosing)
def inner_function():
x = "區域變數" # L(Local)
print(x) # 根據 LEGB 規則,會參考最內層的 x
inner_function()
outer_function()
print(x) # 參考全域作用域的 x
執行結果
區域變數
全域變數
4.2 全域作用域的影響
當你在函式內試圖修改一個全域變數時,如果沒有使用 global
關鍵字,Python 會認為你是在定義區域變數,這可能導致 UnboundLocalError
錯誤。
錯誤示範:在區域作用域中修改全域變數
x = 10 # 全域變數
def update_x():
x += 1 # 發生錯誤
print(x)
update_x()
錯誤訊息
UnboundLocalError: local variable 'x' referenced before assignment
解決方式:使用 global
關鍵字
x = 10 # 全域變數
def update_x():
global x # 明確指定為全域變數
x += 1
print(x)
update_x() # 輸出:11
4.3 使用 nonlocal
修改包覆作用域的變數
如果你想在巢狀函式中修改外層函式的變數(也就是 E 的部分),需要使用 nonlocal
關鍵字。
nonlocal
使用範例
def outer_function():
count = 0 # 外層函式中的變數
def inner_function():
nonlocal count # 指定要使用外層函式的變數
count += 1
print(count)
inner_function()
print(f"最終計數:{count}")
outer_function()
執行結果
1
最終計數:1
4.4 查找內建作用域的函式
Python 內建許多常用函式,如 print()
、len()
等,這些函式存在於最外層的 內建作用域(Built-in Scope)。
覆蓋內建函式的錯誤範例
sum = 100 # `sum()` 是內建函式,但被變數覆蓋
print(sum([1, 2, 3])) # TypeError: 'int' object is not callable
解決方式:刪除覆蓋的變數
del sum # 移除覆蓋的變數
print(sum([1, 2, 3])) # 正常執行
4.5 LEGB 規則總結
作用域 | 說明 | 範例 |
---|---|---|
L(Local) | 函式內定義的變數 | def func(): x = 1 |
E(Enclosing) | 外層函式中定義的變數 | 巢狀函式中的外層變數 |
G(Global) | 模組層級(函式外)定義的變數 | x = 10 |
B(Built-in) | Python 內建函式與變數 | print() , len() |
透過理解 LEGB 規則,你將能更清楚地掌握 Python 如何解析變數,有效避免常見錯誤。
5. 全域變數與區域變數的使用時機
在 Python 中,正確管理變數的全域作用域與區域作用域非常重要。若濫用全域變數,會降低程式的可讀性,並提高產生錯誤的風險。
本節將詳細說明全域變數與區域變數的差異,以及如何正確使用。
5.1 什麼是全域變數?
全域變數(Global Variable) 是指在函式外部定義,整個腳本中都可以參考的變數。
全域變數範例
count = 0 # 全域變數
def increment():
global count # 使用全域變數
count += 1
increment()
print(count) # 輸出:1
5.2 什麼是區域變數?
區域變數(Local Variable) 是指在函式內部定義,只在該函式內有效的變數。
區域變數範例
def my_function():
x = 10 # 區域變數
print(x)
my_function()
print(x) # NameError: name 'x' is not defined
5.3 什麼情況應該使用 global
關鍵字?
通常應該儘量避免使用全域變數,但在某些特定情況下,使用 global
是合適的。
適用情況
- 用來管理設定值或常數
- 當需要在多個函式之間共享變數時
範例:管理設定值
MAX_USERS = 100 # 全域變數(作為常數使用)
def print_max_users():
print(f"最大使用者數量:{MAX_USERS}")
print_max_users() # 輸出:最大使用者數量:100
5.4 不使用 global
管理變數的方法
與其使用全域變數,不如善用函式的參數與回傳值,這樣可以更安全地管理資料。
使用函式參數與回傳值
def increment(value):
return value + 1
counter = 0
counter = increment(counter) # 接收回傳值
print(counter) # 輸出:1
5.5 全域變數與區域變數的比較總結
項目 | 全域變數 | 區域變數 |
---|---|---|
定義位置 | 函式外部 | 函式內部 |
存取範圍 | 整個腳本皆可使用 | 僅限於定義的函式內 |
變更影響 | 可能影響所有函式 | 影響範圍較小 |
優點 | 可在任何地方存取 | 提升函式獨立性 |
缺點 | 變更難以追蹤 | 無法在函式外使用 |
建議用法 | 用於常數或設定值 | 盡量使用區域變數 |
✅ 最佳實踐:盡量減少全域變數的使用,優先使用區域變數。
✅ 如需在函式間傳遞資料,建議透過函式參數與回傳值實現。
6. 巢狀函式與 nonlocal
關鍵字
在 Python 中,可以在一個函式內部定義另一個函式,這稱為巢狀(嵌套)函式。善用巢狀函式可以讓程式結構更清晰,同時更細緻地管理變數作用域。
如果你想在巢狀函式中修改外層函式的變數,就需要使用nonlocal
關鍵字。本節將詳細說明 nonlocal
的用途與使用方法。
6.1 什麼是巢狀函式?
巢狀函式(Nested Function) 是指在函式內部定義的函式。
巢狀函式範例
def outer_function():
def inner_function():
print("這是內部函式")
inner_function() # 在外部函式中呼叫內部函式
outer_function() # 輸出:這是內部函式
6.2 使用 nonlocal
修改外層變數
一般情況下,內部函式無法直接修改外部函式的變數。如果這樣做會發生錯誤。
錯誤範例
def outer_function():
count = 0 # 外部函式的變數
def inner_function():
count += 1 # 發生錯誤
print(count)
inner_function()
outer_function()
解決方式:使用 nonlocal
關鍵字
def outer_function():
count = 0 # 外部函式的變數
def inner_function():
nonlocal count # 指定使用外層函式的變數
count += 1
print(count)
inner_function()
print(count) # 內部修改後的值會反映到這裡
outer_function()
執行結果
1
1
6.3 global
和 nonlocal
的差異
關鍵字 | 影響的作用域 | 用途說明 |
---|---|---|
global | 整個模組的全域變數 | 從函式內部修改全域變數 |
nonlocal | 外層函式的變數(包覆作用域) | 在巢狀函式中修改外層變數 |
global
範例
count = 0 # 全域變數
def increment():
global count # 修改全域變數
count += 1
increment()
print(count) # 輸出:1
nonlocal
範例
def outer():
count = 0 # 包覆作用域中的變數
def inner():
nonlocal count # 修改外層函式的變數
count += 1
inner()
print(count) # 輸出:1
outer()
✅ global
用於修改全域變數。
✅ nonlocal
用於在巢狀函式中修改外層變數。
7. 常見的作用域錯誤與解決方式
如果對 Python 的作用域理解不夠深入,可能會遇到以下問題:「變數未定義」、「意外改變變數的值」、「作用域使用錯誤」 等。
本節將說明與作用域相關的常見錯誤原因及其對應的解決方法。
7.1 UnboundLocalError
:在指定前參考變數
錯誤範例
count = 0 # 全域變數
def increment():
count += 1 # 這裡會發生錯誤
print(count)
increment()
錯誤訊息
UnboundLocalError: local variable 'count' referenced before assignment
解決方案 ①:使用 global
關鍵字
count = 0 # 全域變數
def increment():
global count # 明確指定使用全域變數
count += 1
print(count)
increment() # 輸出:1
解決方案 ②:使用函式參數
def increment(count):
return count + 1
count = 0
count = increment(count)
print(count) # 輸出:1
7.2 NameError
:變數未定義
錯誤範例
def print_message():
print(message) # 這裡會發生錯誤
print_message()
錯誤訊息
NameError: name 'message' is not defined
解決方式
請在函式中定義變數,或透過參數傳入。
def print_message(message):
print(message)
print_message("你好") # 輸出:你好
7.3 TypeError
:使用與內建函式同名的變數
如果你定義的變數名稱與 Python 的內建函式相同,呼叫該函式時可能會發生 TypeError
。
錯誤範例
sum = 100 # 覆蓋了內建函式 sum()
print(sum([1, 2, 3])) # 這裡會發生錯誤
錯誤訊息
TypeError: 'int' object is not callable
解決方式
- 更改變數名稱
total = 100 # 使用 total 代替 sum
print(sum([1, 2, 3])) # 正常執行
- 刪除被覆蓋的內建函式
sum = 100
del sum # 解除覆蓋
print(sum([1, 2, 3])) # 正常執行
7.4 預防作用域錯誤的最佳做法
✅ 盡可能使用區域變數(限制作用範圍可避免意外變更)
✅ 不要在函式內部修改全域變數(避免使用 global
,建議使用參數與回傳值)
✅ 確認未使用與內建函式相同的變數名稱
✅ 清楚規劃變數的作用範圍,並選擇適當的作用域
8. 正確管理作用域的實用技巧
妥善管理 Python 的作用域對於提升程式可讀性與避免錯誤來說非常重要。特別是濫用全域變數或管理不當,會導致程式行為異常並增加除錯難度。
本節將介紹幾個實用的建議,幫助你更好地管理作用域。
8.1 儘量減少全域變數的使用
全域變數可以在任何地方被存取,這使得變數容易被意外修改。因此,建議將全域變數的使用降到最低。
錯誤示範:過度使用全域變數
counter = 0 # 全域變數
def increment():
global counter # 修改全域變數
counter += 1
increment()
print(counter) # 輸出:1
正確示範:使用參數與回傳值
def increment(value):
return value + 1
counter = 0
counter = increment(counter)
print(counter) # 輸出:1
8.2 清晰定義函式的作用域
透過適當管理函式內的變數範圍,可以限制變數的影響範圍,防止意外修改。
良好範例:活用區域變數
def calculate_square(number):
result = number ** 2 # 區域變數
return result
print(calculate_square(4)) # 輸出:16
8.3 正確使用 global
與 nonlocal
使用 global
的合適情境
- 用於設定值或常數的管理
- 需要保留狀態的變數(例如:單例模式)
MAX_USERS = 100 # 全域變數(作為常數)
def get_max_users():
return MAX_USERS
print(get_max_users()) # 輸出:100
使用 nonlocal
的合適情境
- 在巢狀函式中需要修改外層函式的變數時
def outer():
count = 0 # 外層函式變數
def inner():
nonlocal count # 修改外層變數
count += 1
print(count)
inner()
outer() # 輸出:1
8.4 作用域管理檢查清單
✅ 函式內部使用的變數,是否都用區域變數?
✅ 全域變數是否真的有必要?是否可以改用函式參數?
✅ 函式間傳遞資料是否使用了參數與回傳值?
✅ 是否過度使用 global
或 nonlocal
?
✅ 是否使用了與內建函式相同的變數名稱?
✅ 是否有意識地規劃變數的作用範圍?
善用這份檢查清單,可以幫助你在撰寫程式時避免作用域相關的問題,寫出更安全且易於維護的程式碼。
9. FAQ(常見問題與解答)
本節整理了關於 Python 變數作用域的常見問題,
透過問與答的形式解說,幫助你釐清誤解,深化實務理解。
9.1 不使用 global
,要如何管理全域變數?
A:可透過函式的參數與回傳值來處理變數,避免修改全域變數。
def increment(value):
return value + 1
count = 10
count = increment(count)
print(count) # 輸出:11
✅ 這種作法讓函式更獨立,也提升了重用性。
9.2 nonlocal
和 global
有什麼不同?
A: nonlocal
是用來修改「包覆作用域」中的變數。
關鍵字 | 影響的作用域 | 使用場景 |
---|---|---|
global | 全域作用域 | 在函式內修改模組層級的變數 |
nonlocal | 包覆作用域(外層函式) | 在巢狀函式中修改外層函式的變數 |
nonlocal
的範例
def outer():
count = 0 # 外層函式的變數
def inner():
nonlocal count # 修改外層變數
count += 1
inner()
print(count) # 輸出:1
outer()
9.3 什麼是 LEGB 規則
?
A:這是 Python 在搜尋變數時所依循的優先順序。
- L(Local): 函式內部定義的變數
- E(Enclosing): 巢狀函式的外層函式變數
- G(Global): 模組層級的變數
- B(Built-in): Python 的內建函式
LEGB 規則範例
x = "全域變數" # G
def outer():
x = "包覆變數" # E
def inner():
x = "區域變數" # L
print(x) # 根據 LEGB 規則,會輸出最內層的變數
inner()
outer()
print(x) # 參考全域變數
✅ 理解 LEGB 規則,有助於掌握 Python 的變數解析機制。
10. 總結
本文詳細說明了 Python 中的變數作用域(Scope)。透過理解與正確管理作用域,你可以避免 Bug 並撰寫更具可讀性的程式碼。
10.1 Python 作用域的基本概念
✅ 什麼是作用域?
- 決定變數可被存取範圍的規則。
- 區域作用域(函式內)與全域作用域(函式外)是最基本的分類。
✅ 為什麼要理解作用域?
- 防止變數被意外覆蓋。
- 避免變數未定義錯誤(如
NameError
)。 - 提升程式碼的可讀性與除錯效率。
10.2 Python 的四種作用域
Python 中有4 種作用域,變數的搜尋順序依照LEGB 規則:
作用域 | 說明 | 範例 |
---|---|---|
L(Local) | 函式內定義的變數 | def func(): x = 1 |
E(Enclosing) | 外層函式中定義的變數 | 巢狀函式使用外層變數 |
G(Global) | 整個模組中都可使用的變數 | x = 10 (函式外) |
B(Built-in) | Python 預設提供的函式與變數 | print() , len() |
10.3 global
與 nonlocal
的正確用法
✅ 使用 global
的情境:
- 需要集中管理設定值或常數
- 在多個函式中共用相同變數
✅ 使用 nonlocal
的情境:
- 在巢狀函式內修改外層函式的變數
10.4 管理作用域的最佳實踐
✅ 盡量減少全域變數的使用,優先使用區域變數。
✅ 函式間的資料傳遞,優先透過參數與回傳值。
✅ 熟悉 LEGB 規則,意識變數的查找順序。
10.5 結語
正確理解與管理作用域,是提升 Python 編程實力不可或缺的一環。尤其是避免濫用全域變數、明確定義函式的作用範圍,可以讓你寫出更穩定、可維護的程式。
掌握作用域,讓你的 Python 程式碼更安全、更專業、更易維護! 🚀