Python 變數作用域 (Scope)
變數作用域決定了變數在程式碼中可以被存取的範圍。Python 有四種作用域:Local、Enclosing、Global、Built-in(LEGB 規則)。
LEGB 規則
Python 查找變數時,會按照以下順序:
- Local(區域):函數內部定義的變數
- Enclosing(外層函數):外層函數的區域變數(用於巢狀函數)
- Global(全域):模組層級定義的變數
- Built-in(內建):Python 內建的名稱(如
print、len)
x = "global" # Global
def outer():
x = "enclosing" # Enclosing
def inner():
x = "local" # Local
print(x) # local
inner()
print(x) # enclosing
outer()
print(x) # global
Local Scope(區域作用域)
在函數內部定義的變數只在該函數內有效:
def my_function():
x = 10 # 區域變數
print(x)
my_function() # 10
# print(x) # NameError: name 'x' is not defined
每次呼叫函數,區域變數都會重新建立:
def counter():
count = 0
count += 1
return count
print(counter()) # 1
print(counter()) # 1(每次都是新的 count)
Global Scope(全域作用域)
在所有函數之外定義的變數是全域變數,可以在任何地方讀取:
name = "Alice" # 全域變數
def greet():
print(f"Hello, {name}!") # 可以讀取全域變數
greet() # Hello, Alice!
修改全域變數
在函數內要修改全域變數,必須使用 global 關鍵字:
count = 0
def increment():
global count # 聲明使用全域變數
count += 1
increment()
increment()
print(count) # 2
如果不使用 global,會建立一個同名的區域變數:
count = 0
def increment():
count = 100 # 這是新的區域變數,不是全域的 count
print(f"Inside: {count}")
increment() # Inside: 100
print(f"Outside: {count}") # Outside: 0
過度使用全域變數會讓程式難以維護和除錯。盡量使用參數傳遞和回傳值。
Enclosing Scope(外層函數作用域)
巢狀函數可以存取外層函數的變數:
def outer():
message = "Hello" # 外層函數的變數
def inner():
print(message) # 可以讀取外層變數
inner()
outer() # Hello
修改外層變數
要修改外層函數的變數,使用 nonlocal 關鍵字:
def outer():
count = 0
def inner():
nonlocal count # 聲明使用外層變數
count += 1
print(f"Count: {count}")
inner() # Count: 1
inner() # Count: 2
inner() # Count: 3
outer()
閉包 (Closure)
函數可以「記住」它被定義時的外層變數:
def create_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter1 = create_counter()
counter2 = create_counter()
print(counter1()) # 1
print(counter1()) # 2
print(counter1()) # 3
print(counter2()) # 1(獨立的計數器)
Built-in Scope(內建作用域)
Python 內建的函數和名稱,如 print、len、range 等:
# 可以覆蓋內建名稱(但不建議)
print = "Hello"
# print("test") # TypeError!
# 恢復內建函數
del print
print("test") # test
不要用內建函數的名稱作為變數名稱:
# 不好的做法
list = [1, 2, 3] # 覆蓋了內建的 list
dict = {"a": 1} # 覆蓋了內建的 dict
str = "hello" # 覆蓋了內建的 str
查看作用域中的變數
x = "global"
def my_function():
y = "local"
print("Local:", locals()) # 區域變數
print("Global:", globals()) # 全域變數
my_function()
實際範例
設定選項
# 全域設定(謹慎使用)
DEBUG = False
def set_debug(value):
global DEBUG
DEBUG = value
def log(message):
if DEBUG:
print(f"[DEBUG] {message}")
log("Test") # 不輸出
set_debug(True)
log("Test") # [DEBUG] Test
建立工廠函數
def create_multiplier(factor):
"""建立一個乘法函數"""
def multiplier(number):
return number * factor # 記住外層的 factor
return multiplier
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
快取裝飾器
def cache(func):
"""簡單的快取裝飾器"""
cached_results = {} # 外層變數
def wrapper(*args):
if args not in cached_results:
cached_results[args] = func(*args)
return cached_results[args]
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100)) # 很快就能算出來
最佳實踐
- 盡量避免全域變數:使用參數傳遞和回傳值
- 縮小作用域:變數應該在最小的必要範圍內定義
- 不要覆蓋內建名稱:避免使用
list、dict、str等作為變數名 - 謹慎使用 global 和 nonlocal:它們會增加程式碼的複雜度
# 不好的做法
result = None
def calculate():
global result
result = 42
calculate()
print(result)
# 好的做法
def calculate():
return 42
result = calculate()
print(result)