Programmer debugging Python code with namespaces and scope visualization tools

Python 作用域 (Scope)(17):名稱能在哪裡被找到?

在上一篇文章中,我們介紹了 命名空間 (Namespace),了解了名稱與對象如何被儲存與管理。


不過,光知道名稱存在哪裡還不夠,程式還需要一套規則來決定「在什麼位置可以使用這些名稱」。這就是 作用域 (Scope) 的任務。

作用域定義了名稱的可見範圍與存活時間。無論是函數中的局部變數、模組中的全域變數,還是 Python 內建的函數,程式在執行時會依照特定的搜尋順序來解析它們。

作用域 (Scope) 介紹

作用域定義了程式在不同部分可以訪問哪些命名空間,以及訪問的順序。

作用域決定了我們程式中哪些名稱可以被訪問

多個命名空間可能同時存在,但並非所有命名空間在程式的各個部分都可訪問

與命名空間類似,Python 有四種不同層級的作用域:內建作用域、全域作用域、封閉作用域和局部作用域

命名空間是存儲名稱-對象對的機制,而作用域則是一套規則系統,決定在程式的哪些位置可以檢索這些名稱。

A diagram showing nested circles representing different scopes in Python, with arrows indicating access patterns between Local, Enclosing, Global, and Built-in scopes

局部作用域 (Local Scope)

局部作用域的特點:

  • 每次呼叫函數時產生
  • 是最深層的作用域
  • 局部作用域中的名稱不能被外部作用域訪問或修改
  • 通常與局部命名空間一一對應

局部作用域限制了變數的可見性,防止不同函數間的變數互相干擾。

def favorite_color():
  color = 'Red'
print(color)  # 錯誤!

# 正確做法:
def favorite_color():
  color = 'Red'
  print(color)  # 正確
favorite_color()
    

第一個例子中,print(color) 在函數外部,無法訪問函數內的局部作用域。

第二個例子中,print(color) 在函數內部,可以訪問到局部變數 color

封閉/非局部作用域 (Enclosing/Nonlocal Scope)

封閉作用域(也稱為非局部作用域)允許嵌套函數訪問外部函數中定義的值。

def outer_function():
  enclosing_value = 'Enclosing Value'
  def nested_function():
    nested_value = 'Nested Value'
    print(enclosing_value)  # 可以訪問外部函數的變數
  nested_function()
outer_function()

封閉作用域有兩個重要限制:

單向流動

作用域訪問只能向上流動。內部函數可以訪問外部函數的變數,但外部函數不能訪問內部函數的變數。

不可修改性

內部函數可以訪問外部函數的不可變對象(如字符串或數字),但無法直接修改它們。

修改作用域行為:nonlocal 語句

Python 提供了 nonlocal 語句,允許我們在嵌套函數中修改外部函數中定義的變數。

不使用 nonlocal:

def enclosing_function():
  var = "value"
  def nested_function():
    var = "new_value"  # 創建新的局部變數
  nested_function()
  print(var)  # 輸出 "value"
enclosing_function()
    

這裡的 var = "new_value" 只是在內部函數中創建了一個新的局部變數,而不是修改外部函數中的 var

使用 nonlocal:

def enclosing_function():
  var = "value"
  def nested_function():
    nonlocal var
    var = "new_value"  # 修改外部變數
  nested_function()
  print(var)  # 輸出 "new_value"
enclosing_function()
    

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

全域作用域 (Global Scope)

全域作用域的特點:

  • 全域作用域是最高級別的訪問層級
  • 全域命名空間中定義的名稱自動具有全域作用域
  • 全域作用域的名稱可以在程式的任何地方訪問
  • 與封閉作用域類似,默認情況下全域變數可以被訪問但不能被修改
# 全域變數
gravity = 9.8

def get_force(mass):
  return mass * gravity  # 訪問全域變數

print(get_force(60))  # 輸出 588.0
    

全域變數像重力一樣,在整個程式中都有影響,但在局部作用域中嘗試修改它會導致錯誤。

修改作用域行為:global 語句

Python 提供了 global 語句,允許在局部作用域中修改全域變數。

不使用 global

global_var = 10

def some_function():
  global_var = 20  # 創建局部變數

some_function()
print(global_var)  # 輸出 10
    

使用 global

global_var = 10

def some_function():
  global global_var
  global_var = 20  # 修改全域變數

some_function()
print(global_var)  # 輸出 20
    

此外,即使全域命名空間中尚未定義某個名稱,也可以使用 global 語句在全域命名空間中創建它:

def some_function():
 global x # 在全域命名空間中創建 x
 x = 30

some_function()
print(x) # 輸出 30

作用域解析:LEGB 規則

作用域解析是描述 Python 在各種命名空間中搜索名稱的過程。Python 遵循 LEGB 規則,按以下順序搜索名稱:

L (Local)

最先搜索局部作用域(函數內部)

E (Enclosing)

如果在局部找不到,則搜索封閉作用域(外部函數)

G (Global)

如果在封閉作用域找不到,則搜索全域作用域

B (Built-in)

最後搜索內建作用域(Python 預定義的名稱)

如果在這四個作用域中都找不到名稱,Python 會返回 NameError 異常。

Python LEGB scope diagram showing Local, Enclosing, Global, and Built-in scopes as nested circles.

LEGB 規則:實例演示

情況 1:名稱在全域作用域中

age = 27

def func():
  def inner_func():
    print(age)  # 使用全域變數
  inner_func()

func()  # 輸出 27
    

搜索順序:
1. 在 inner_func() 的局部作用域中搜索 age
2. 在 func() 的封閉作用域中搜索 age
3. 在全域作用域中找到 age

情況 2:名稱在多個作用域中

age = 27

def func():
  age = 42  # 在封閉作用域中定義 age
  def inner_func():
    print(age)  # 使用最近的 age
  inner_func()

func()  # 輸出 42
    

搜索順序:
1. 在 inner_func() 的局部作用域中搜索 age
2. 在 func() 的封閉作用域中找到 age,值為 42
3. 不繼續往上搜索

結語

作用域 (Scope) 就像是 Python 的搜尋指南,決定了名稱在哪裡可見、在哪裡不可見。

透過 Local、Enclosing、Global、Built-in 四個層級,Python 建立了一套嚴謹的規則,避免變數互相干擾,也讓程式更具結構性。
理解作用域能幫助你更好地排查錯誤,例如 NameError 或「變數值不如預期」的情況;同時也能讓你靈活運用 globalnonlocal 關鍵字,在需要時修改變數的作用範圍。

Programmer debugging Python code with namespaces and scope visualization tools

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

High Quality

Lorem ipsum dolor sit amet, consectetur adipiscing elitsed do eiusmod tempor.

Fast Delivery

Lorem ipsum dolor sit amet, consectetur adipiscing elitsed do eiusmod tempor.

Best Warranty

Lorem ipsum dolor sit amet, consectetur adipiscing elitsed do eiusmod tempor.