深入理解 Python 的作用域與 LEGB 原則

更新日期: 2024 年 11 月 18 日

本文為 python 作用域系列文,第一篇:

在學習 Python 的過程中,理解作用域(Scope)是非常重要的。

作用域決定了變數的可見性和生命週期。

Python 採用了一套稱為 LEGB 的命名空間查找規則,這對於理解變數是如何被解析的至關重要。

本文將為新手詳細介紹 Python 的作用域以及 LEGB 原則。


什麼是作用域?

作用域是指在程式中可以訪問變數名稱的區域。

根據變數定義的位置不同,其作用域也不同。

Python 中的作用域主要分為以下幾種類型:

  1. Local(本地作用域):函式內部的命名空間,包括函式內定義的變數和參數。
  2. Enclosing(嵌套作用域):外部嵌套函式的本地作用域(在嵌套函式中)。
  3. Global(全域作用域):模組級別的命名空間,包含模組中定義的變數和函式。
  4. Built-in(內建作用域):Python 解譯器內置的命名空間,包含內建函式和例外。

LEGB 原則詳解

LEGBLocalEnclosingGlobalBuilt-in 的縮寫。

當 Python 解析變數名稱時,會按照這個順序進行查找:

  1. Local(本地作用域):首先,Python 會在本地作用域中查找變數,即當前函式內部。
  2. Enclosing(嵌套作用域):如果在本地作用域中沒有找到,會在外層函式的本地作用域中查找(適用於嵌套函式)。
  3. Global(全域作用域):如果還是沒有找到,會在全域作用域中查找,即模組級別。
  4. Built-in(內建作用域):最後,Python 會在內建作用域中查找。

例子解析

本地作用域(Local Scope)

def my_function():
    x = 10
    print(x)

my_function()  # 輸出:10
print(x)  # NameError: name 'x' is not defined

在這個例子中,變數 x 定義在函式 my_function 的本地作用域內,函式外部無法訪問。


全域作用域(Global Scope)

x = 5

def my_function():
    print(x)

my_function()  # 輸出:5
print(x)  # 輸出:5

變數 x 定義在全域作用域內,函式內部可以訪問全域變數。


修改全域變數

x = 5

def my_function():
    global x
    x = 10
    print(x)

my_function()  # 輸出:10
print(x)  # 輸出:10

使用 global 關鍵字,可以在函式內部修改全域變數。

補充說明:global 關鍵字只能宣告

global 關鍵字的使用不允許在同一行進行賦值操作。

例如,這種形式是非法的:

<code>python global a = 2 # 錯誤!</code>

解釋:

  • global 的作用
    • 告訴 Python 你想在函式內部使用並修改定義在全域範圍的變數。
    • global 只能用來宣告變數是全域的,不能同時進行賦值。
    • global 只能單獨一行寫,後續的賦值需要分開進行。
a = 1

def hi():
    global a  # 宣告 a 為全域變數
    a = 2     # 修改全域變數的值
    print(a)

hi()

延伸閱讀:深入理解 Python 中的 global 與 nonlocal 關鍵字


嵌套作用域(Enclosing Scope)

def outer_function():
    x = 'Hello'

    def inner_function():
        print(x)

    inner_function()

outer_function()  # 輸出:Hello

在這個例子中,inner_function 可以訪問 outer_function 中的變數 x


閉包中的變數

def outer_function():
    x = 'Hello'

    def inner_function():
        x = 'Hi'
        print(x)

    inner_function()
    print(x)

outer_function()
# 輸出:
# Hi
# Hello

inner_function 中,定義了一個與外部同名的變數 x,這個變數屬於本地作用域,不會影響外部的 x


使用 nonlocal 關鍵字

def outer_function():
    x = 'Hello'

    def inner_function():
        nonlocal x
        x = 'Hi'
        print(x)

    inner_function()
    print(x)

outer_function()
# 輸出:
# Hi
# Hi

使用 nonlocal 關鍵字,可以在內部函式中修改外部函式的變數。


內建作用域(Built-in Scope)

def hi():
    def print():
        pass
    def hey():
        print(123);
    hey()

hi();

當執行 print(123) 時,Python 的查找順序是:

  1. Local(L):
    • hey 的本地作用域中查找,但沒有找到 print
  2. Enclosing(E):
    • hi 的作用域中查找,找到的是本地定義的 print(覆蓋了內建的 print 函式)。
    • 此時,print 是一個空函式,沒有功能。
  1. Global(G):
    • 如果 hi 的作用域中沒有找到,會在全域作用域中尋找 print,但因為在 Enclosing 找到了,所以不會繼續查找。
  1. Built-in(B):
    • 如果在以上作用域都找不到,Python 最後會在內建作用域中尋找 print

在上述的代碼中,因為 Enclosing 找到了一個覆蓋的 print,而這個函式沒有實現功能,導致執行 print(123) 時出現問題。


注意事項

  • 避免過度使用全域變數:全域變數可能導致程式碼難以維護和除錯。
  • 瞭解作用域的優先順序:理解 LEGB 原則有助於避免命名衝突和意外的行為。
  • 善用關鍵字globalnonlocal 關鍵字在需要修改外部作用域的變數時非常有用。

結論

理解 Python 中的作用域和 LEGB 原則是編寫高品質程式碼的基礎。

通過掌握這些知識,你可以更好地控制變數的可見性和生命週期,避免常見的錯誤。


進一步學習

  • 深入學習閉包(Closure):瞭解函式如何捕獲外部變數。
  • 裝飾器(Decorator):利用作用域和閉包的高級應用。
  • 命名空間(Namespace):更深入地理解變數的存儲方式。

Similar Posts