因為之後想要探討 python 的 decorator,所以今天想先來探討一下它背後的原理,Python 的函示運作以及一個概念 "Closure"(閉包)。
首先要先簡單介紹一下 Python 的 函式
1. 普通函式 (Regular Functions)
這是 Python 中最基本的函式形式,接收參數並返回值。
def hello(name):
return f"Hello, {name}!"
print(hello("Yusinz")) # Output: Hello, Yusinz!
2. 函式作為變數被傳遞 (First-Class Functions)
函式在 python中是一級公民,也被稱為 First-class function,它可以做為變數去傳遞,甚至作為其他函式的參數或返回值。
def hello(name):
return (f"Hello, {name}!")
say_hi = hello
print(say_hi("Yusinz")) # # Output: Hello, Yusinz!
3. 函式可以作為參數 (Function as Arguments)
函式可以被傳遞給另一個函式,這也是 closure 和 decorator 可以實現的重要概念。
def call_func(func):
return func("Yusinz")
def hello(name):
return (f"Hello, {name}!")
print(call_func(hello)) # Output: Hello, Yusinz!
4. 函式可以返回函式 (Function Returning Functions)
一個函式可以返回另一個函式。而外部函式返回內部函式正是閉包的核心。
def outer_function():
def inner_function():
return "Hello!, Yusinz!"
return inner_function
hello = outer_function()
print(hello()) # Output: Hello, Yusinz!
5. 作用域和變數的可見性 (Scope and Variable Visibility)
區域變數或是全域變數大家應該耳熟能詳,就不多作介紹。
enclosing Scope
作用域:在函式內部的函式中。
可見性:內部函式可以訪問外部函式的變數。
Built-in Scope
作用域:Python 的內建名稱和函式,如 print()
、len()
等。
可見性:在整個 Module 中都可見,即使在其他作用域內部也可以訪問。
global_name = "Global Yusinz"
def hello():
enclosing_name = "Enclosing Yusinz"
def say_hello():
local_name = "Local Yusinz"
print(f"Hello {global_name}!") #Output: Hello Global Yusinz!
print(f"Hello {enclosing_name}!") #Output: Hello Local Yusinz!
print(f"Hello {local_name}!") #Output: Hello Enclosing Yusinz!
say_hello()
hello()
了解 Python 函式概念後,
理解這些基本概念後,就能快速的理解閉包( Closure ),因為閉包正是利用了函式作為一級公民的特性,並且依賴於作用域的概念來保存外部函式的變數。
閉包(closure)是指當外層的函式把一個內層的函式返回時,它會「記住」它所在的外層函式的變數,即使外層函式已經執行完畢。這讓內層函式能在外層函式結束後,繼續使用外部函式中的變數。
def outer_function():
name = "Yusinz"
def inner_function():
print(f"Hello, {name}!")
return inner_function
closure_example = outer_function()
closure_example() # Output: Hello, Yusinz!
當我們 call closure_example()
時,outer_function
會返回 inner_function
,照理來說我們返回的 inner_function
裡面的 name 在執行完 outer_function
後生命週期就結束,我們單純要 closure_example()
去 call inner_function()
他應該讀不到外層的 name
才對,但實際執行後會發現,他還是可以印出 Hello, Yusinz!
這是因為返回的 inner_function
是一個 closure
,它可以記憶住 name
的值。所以就算 outer_function
已經執行完畢了,closure 仍然可以使用 name
。
這個被記憶住的變數 name,我們可以叫他 captured variable,使用 captured variable 的函式 inner_function
和 closure_example
就是 closure。
captured variable 在使用時,如果需要賦值,會出現 UnboundLocalError,這是因為賦值之後 captured variable 會被轉換為區域變數,這時候它就會拿不到外層函式的變數。但要解決也很簡單使用 nonlocal 宣告變數後就能對它做操作了。
以 Counter 計數器為例
def create_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter = create_counter()
print(counter()) # Output: 1
print(counter()) # Output: 2
你可能會想,那 closure 可以很多個嗎?
答案是可以,每個 closure 都是獨立的,以 counter 為例,可以看到 second_counter()
的值還會是 1。
def create_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter = create_counter()
second_counter = create_counter()
print(counter()) # Output: 1
print(counter()) # Output: 2
print(counter()) # Output: 3
print(second_counter()) # Output: 1
最後,一樣建議大家可以實際去操作,才能更了解其中的用法,這裡一樣附上可以線上練習的 place。