Python 變數作用域

Python 變數作用域(Python Variable Scope)

這篇 Python 作用域教學將回答一個常見疑問:「為什麼函式裡面改了變數,外面卻沒變?」變數不是在哪裡都能存取的,它有「生效範圍」,這就是作用域(Scope)。搞懂這個,很多莫名其妙的 bug 會瞬間清楚。

區域變數與全域變數

種類定義位置生效範圍
區域變數(local)函式內只在該函式內
全域變數(global)函式外(模組層級)整個檔案
message = "我是全域變數"

def show():
    inner = "我是區域變數"
    print(message)    # 函式內可以「讀」全域變數
    print(inner)

show()
print(message)
# print(inner)        # 這行會報 NameError:區域變數外面看不到

執行輸出:

我是全域變數
我是區域變數
我是全域變數

函式內指派 = 建立新的區域變數

關鍵規則:在函式內對變數指派,Python 就視它為區域變數,跟外面的同名變數無關:

count = 0

def add_one():
    count = 1      # 這是「新的區域變數」,不是修改全域的 count
    print("函式內:", count)

add_one()
print("函式外:", count)    # 全域的 count 沒變

執行輸出:

函式內: 1
函式外: 0

global:真的要改全域變數時

count = 0

def add_one():
    global count    # 宣告:我要用的是全域的 count
    count = count + 1

add_one()
add_one()
print(count)

執行輸出:

2

能用就少用——到處 global 的程式很難追蹤誰改了什麼。比較好的做法是用參數傳入、用 return 傳出:

def add_one(count):
    return count + 1

count = 0
count = add_one(count)
count = add_one(count)
print(count)

執行輸出:

2

LEGB 查找規則與 nonlocal

Python 找變數的順序是 LEGB:Local(函式內)→ Enclosing(外層函式)→ Global(模組)→ Built-in(內建)。巢狀函式要修改「外層函式」的變數時,用 nonlocal

def counter():
    n = 0
    def add():
        nonlocal n     # 指向外層函式的 n,不是全域
        n = n + 1
        return n
    return add

c = counter()
print(c())
print(c())

執行輸出:

1
2

常見錯誤

1. UnboundLocalError: cannot access local variable ‘x’

函式內「先讀後指派」同一個變數:因為函式內有指派,整個函式裡它都被當成區域變數,但讀取時還沒建立。要改全域就加 global,否則改用參數傳入。

x = 10
def f():
    print(x)    # UnboundLocalError!因為下一行有指派
    x = 20

2. 蓋掉內建名稱

把變數取名為 listsummax 等內建函式名,之後呼叫 list() 就會報 TypeError: 'list' object is not callable。避開內建名稱(LEGB 的 B 被你蓋掉了)。

3. 以為函式能改外部的數字或字串

傳進函式的 int、str 等不可變型別,在函式內重新指派不會影響外部,要用 return 把新值傳回來。

總結

函式內指派就是區域變數;讀全域可以、改全域要 global(但盡量用參數與 return 代替);查找順序 LEGB;巢狀函式改外層用 nonlocal。理解「先讀後指派」就能秒懂 UnboundLocalError。下一篇是讓 sort 與 max 變聰明的 lambda 匿名函式。

延伸閱讀