PYTHON 入門|函式如何記住資料
Python Closure 閉包:5 個簡單又經典的範例
Closure(閉包)讓內層函式在外層函式結束後,仍能記住並使用外層函式建立的資料。它常用來保存狀態、建立可設定的函式,也能幫助我們理解裝飾器的運作方式。
簡單記法:閉包通常包含外層函式、內層函式,以及內層函式所記住的外層變數。
一、閉包的運作流程
外層函式建立資料和內層函式,接著把內層函式回傳給外部。外層函式結束後,回傳的函式仍保有它需要的外層資料。
二、範例 1:閉包記住名字
def make_greeter(name):
def greet():
print(f"你好,{name}!")
return greet
hello = make_greeter("小明")
hello()
執行結果
你好,小明!
程式解說
- 呼叫
make_greeter("小明")時,外層函式的參數name取得「小明」。 - 內層函式
greet()使用了外層的name。 return greet回傳函式本身,注意這裡沒有加括號;若寫成return greet(),就會立刻執行它,而不是回傳函式。- 回傳的函式存入
hello。之後呼叫hello(),它仍能讀取閉包保留的名字。
三、範例 2:建立不同的問候函式
def make_greeter(name):
def greet():
print(f"你好,{name}!")
return greet
greet_ming = make_greeter("小明")
greet_hua = make_greeter("小華")
greet_ming()
greet_hua()
執行結果
你好,小明!
你好,小華!
程式解說
- 第一次呼叫
make_greeter,建立一個記住「小明」的閉包。 - 第二次呼叫時,建立另一個記住「小華」的閉包。
greet_ming和greet_hua是不同的函式物件,各自保存自己的name。- 因此呼叫其中一個,不會改變另一個閉包記住的資料。
四、範例 3:建立會累加的計數器
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter = make_counter()
print(counter())
print(counter())
print(counter())
執行結果
1
2
3
程式解說
make_counter()把count初始化為 0,並定義內層函式counter()。- 每次呼叫內層函式,
count += 1就把原本記住的值加一,再回傳新值。 nonlocal count告訴 Python:這裡要修改最近一層外部函式中的count,而不是建立新的區域變數。- 如果移除
nonlocal,因為程式在函式內對count賦值,Python 會把它視為區域變數;在讀取它之前尚未設定值,會發生UnboundLocalError。
五、範例 4:各自保存起始值的計數器
def make_counter(start):
count = start
def counter():
nonlocal count
count += 1
return count
return counter
counter_a = make_counter(0)
counter_b = make_counter(10)
print(counter_a())
print(counter_a())
print(counter_b())
執行結果
1
2
11
程式解說
- 這個版本讓建立計數器時指定起始值,
count = start把起始值放進閉包。 counter_a記住自己的count = 0;counter_b記住自己的count = 10。- 呼叫
counter_a()兩次,得到 1、2;呼叫counter_b()一次,則從 10 加到 11。 - 每次執行
make_counter()都會建立一組新的外層環境,所以兩個計數器的狀態互不干擾。
六、範例 5:建立簡單的乘法器
def make_multiplier(number):
def multiply(value):
return number * value
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))
print(triple(5))
執行結果
10
15
程式解說
make_multiplier(2)回傳一個記住number = 2的函式,存入double。make_multiplier(3)回傳另一個記住number = 3的函式,存入triple。- 呼叫
double(5)時,內層函式把 2 與 5 相乘;呼叫triple(5)時,則把 3 與 5 相乘。 - 這種寫法可以建立「已預先設定部分資料」的函式。
七、閉包重點整理
- 內層函式使用了外層函式的變數,並且被保留下來,就形成閉包。
- 閉包可以在外層函式結束後,繼續使用外層變數。
- 每次呼叫外層函式都可以建立一個有獨立狀態的新閉包。
- 要在內層函式修改外層函式的變數,使用
nonlocal。 - 閉包常用於保存狀態、建立函式工廠、設定回呼行為,以及實作裝飾器。
學習閉包時,可以先追蹤三件事:外層函式建立了什麼資料、內層函式使用了哪些資料,以及外層函式結束後是哪個回傳函式仍保有這些資料。掌握這個關係,就能看懂許多 Python 程式中的閉包寫法。
沒有留言:
張貼留言