Python 變數作用域完全指南|深入解析 LEGB 規則、global、nonlocal!

目次

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 的作用域,請掌握以下幾個要點:

  1. 函式內定義的變數只在該函式內有效(區域作用域)
  2. 函式外定義的變數可在整個腳本中使用(全域作用域)
  3. Python 使用「LEGB 規則」來決定變數的搜尋順序
  4. 若要在函式中修改全域變數,需使用 global 關鍵字
  5. 若要在巢狀函式中修改外層變數,需使用 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

說明:
printlen 是 Python 的內建函式,無論在哪個作用域都可以使用。

3.5 四種作用域總結

作用域名稱說明範例
區域作用域僅在函式內部有效的變數函式內的變數
包覆作用域可由巢狀函式存取外層函式的變數巢狀函式中的變數
全域作用域整個腳本中皆可參考的變數global 變數
內建作用域Python 內建函式與變數print(), len()

4. 變數作用域的查找順序(LEGB 規則)

在 Python 中,當你參考某個變數時,會依照特定的順序來查找變數。這個查找順序稱為 LEGB 規則(Local、Enclosing、Global、Built-in),查找順序如下:

  1. L(Local)區域作用域 – 目前函式內定義的變數
  2. E(Enclosing)包覆作用域 – 外層(巢狀)函式中定義的變數
  3. G(Global)全域作用域 – 模組層級定義的變數
  4. 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 globalnonlocal 的差異

關鍵字影響的作用域用途說明
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

解決方式

  1. 更改變數名稱
total = 100  # 使用 total 代替 sum
print(sum([1, 2, 3]))  # 正常執行
  1. 刪除被覆蓋的內建函式
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 正確使用 globalnonlocal

使用 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 作用域管理檢查清單

函式內部使用的變數,是否都用區域變數?
全域變數是否真的有必要?是否可以改用函式參數?
函式間傳遞資料是否使用了參數與回傳值?
是否過度使用 globalnonlocal
是否使用了與內建函式相同的變數名稱?
是否有意識地規劃變數的作用範圍?

善用這份檢查清單,可以幫助你在撰寫程式時避免作用域相關的問題,寫出更安全且易於維護的程式碼。

9. FAQ(常見問題與解答)

本節整理了關於 Python 變數作用域的常見問題
透過問與答的形式解說,幫助你釐清誤解,深化實務理解。

9.1 不使用 global,要如何管理全域變數?

A:可透過函式的參數與回傳值來處理變數,避免修改全域變數。

def increment(value):
    return value + 1

count = 10
count = increment(count)
print(count)  # 輸出:11

這種作法讓函式更獨立,也提升了重用性。

9.2 nonlocalglobal 有什麼不同?

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 在搜尋變數時所依循的優先順序。

  1. L(Local): 函式內部定義的變數
  2. E(Enclosing): 巢狀函式的外層函式變數
  3. G(Global): 模組層級的變數
  4. 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 globalnonlocal 的正確用法

使用 global 的情境:

  • 需要集中管理設定值或常數
  • 在多個函式中共用相同變數

使用 nonlocal 的情境:

  • 在巢狀函式內修改外層函式的變數

10.4 管理作用域的最佳實踐

盡量減少全域變數的使用,優先使用區域變數。
函式間的資料傳遞,優先透過參數與回傳值。
熟悉 LEGB 規則,意識變數的查找順序。

10.5 結語

正確理解與管理作用域,是提升 Python 編程實力不可或缺的一環。尤其是避免濫用全域變數、明確定義函式的作用範圍,可以讓你寫出更穩定、可維護的程式。

掌握作用域,讓你的 Python 程式碼更安全、更專業、更易維護! 🚀