深入理解 Python 的作用域與 LEGB 原則
更新日期: 2024 年 11 月 18 日
本文為 python 作用域系列文,第一篇:
- 基礎:深入理解 Python 的作用域與 LEGB 原則
- 1 階進化:深入理解 Python 中的 global 與 nonlocal 關鍵字
- 2 階進化:深入理解 Python 中的閉包(Closure)
在學習 Python 的過程中,理解作用域(Scope)是非常重要的。
作用域決定了變數的可見性和生命週期。
Python 採用了一套稱為 LEGB 的命名空間查找規則,這對於理解變數是如何被解析的至關重要。
本文將為新手詳細介紹 Python 的作用域以及 LEGB 原則。
什麼是作用域?
作用域是指在程式中可以訪問變數名稱的區域。
根據變數定義的位置不同,其作用域也不同。
Python 中的作用域主要分為以下幾種類型:
- Local(本地作用域):函式內部的命名空間,包括函式內定義的變數和參數。
- Enclosing(嵌套作用域):外部嵌套函式的本地作用域(在嵌套函式中)。
- Global(全域作用域):模組級別的命名空間,包含模組中定義的變數和函式。
- Built-in(內建作用域):Python 解譯器內置的命名空間,包含內建函式和例外。
LEGB 原則詳解
LEGB 是 Local、Enclosing、Global、Built-in 的縮寫。
當 Python 解析變數名稱時,會按照這個順序進行查找:
- Local(本地作用域):首先,Python 會在本地作用域中查找變數,即當前函式內部。
- Enclosing(嵌套作用域):如果在本地作用域中沒有找到,會在外層函式的本地作用域中查找(適用於嵌套函式)。
- Global(全域作用域):如果還是沒有找到,會在全域作用域中查找,即模組級別。
- 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 的查找順序是:
- Local(L):
- 在
hey
的本地作用域中查找,但沒有找到print
。
- 在
- Enclosing(E):
- 在
hi
的作用域中查找,找到的是本地定義的print
(覆蓋了內建的print
函式)。 - 此時,
print
是一個空函式,沒有功能。
- 在
- Global(G):
- 如果
hi
的作用域中沒有找到,會在全域作用域中尋找print
,但因為在 Enclosing 找到了,所以不會繼續查找。
- 如果
- Built-in(B):
- 如果在以上作用域都找不到,Python 最後會在內建作用域中尋找
print
。
- 如果在以上作用域都找不到,Python 最後會在內建作用域中尋找
在上述的代碼中,因為 Enclosing 找到了一個覆蓋的 print
,而這個函式沒有實現功能,導致執行 print(123)
時出現問題。
注意事項
- 避免過度使用全域變數:全域變數可能導致程式碼難以維護和除錯。
- 瞭解作用域的優先順序:理解 LEGB 原則有助於避免命名衝突和意外的行為。
- 善用關鍵字:
global
和nonlocal
關鍵字在需要修改外部作用域的變數時非常有用。
結論
理解 Python 中的作用域和 LEGB 原則是編寫高品質程式碼的基礎。
通過掌握這些知識,你可以更好地控制變數的可見性和生命週期,避免常見的錯誤。
進一步學習
- 深入學習閉包(Closure):瞭解函式如何捕獲外部變數。
- 裝飾器(Decorator):利用作用域和閉包的高級應用。
- 命名空間(Namespace):更深入地理解變數的存儲方式。