理解Python函式閉包
本文主要介紹什麼是閉包,Ptyhon中使用閉包時容易出現的變數問題。
閉包
閉包指延伸了作用域的函式,其中包含函式定義體中引用、但是不在定義體中定義的非全域性變數。函式是不是匿名的沒有關係,關鍵是它能訪問定義體之外定義的非全域性變數。
舉個栗子
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
avg = make_averager()
avg(10) # 10.0
avg(13) # 11.5
avg(19) # 14.0
上面定義了一個巢狀函式,作用是計算移動平均值。
呼叫make_averager
時,返回一個averager
函式物件。每次呼叫averager
時,會把一個新的引數值新增到列表中,然後計算列表的平均值。series
是make_averager
函式的區域性變數,但是呼叫avg(10)
時,make_averager()
函式已經返回了,所以它的本地作用域也沒有了。
在averager()
函式中,series
變成了自由變數,就是沒在本地作用域中繫結的變數。
所以閉包是一種函式,它會保留定義函式時存在的自由變數的繫結,在呼叫函式時,雖然定義作用域不可用了但是還可以使用繫結的變數。注意,只有巢狀在其他函式中的函式才可能需要處理不在全域性作用域中的外部變數
栗子改進
雖然上面函式可以實現計算移動平均,但是效率不高,因為把所有歷史資料都保留在列表中。如果只儲存總和以及元素個數,再求平均值更好些。
def make_averager():
count = 0
total = 0
def averager(new_value):
count += 1
total += new_value
return total / count
return averager
上述函式在呼叫averager()
時是有問題的,原因是count
初始定義是數字,是不可變型別。在執行count = count + 1
時,count
變成了區域性變數,不是自由變數,所以解析器認為count
在averager()
裡沒有定義。total
變數也是這樣的問題。
問題顯然出現在變數是否是可變型別上,在用列表計算的時候,採用的series
是可變的列表型別。但是對於數字、字串、元祖等不可變型別,巢狀函式內只能讀取,不能更新,如果嘗試重新繫結,比如count = count + 1
,就會隱式建立區域性變數,不會成為自由變數儲存在閉包裡。
python3下解決方式是引入nonlocal
宣告。作用就是把變數標記為自由變數。
def make_averager():
count = 0
total = 0
def averager(new_value):
nonlocal count,total
count += 1
total += new_value
return total / count
return averager
總結
在函式內巢狀的函式如果引用了外層函式定義的變數,外部呼叫巢狀的函式時,可以認為這時候函式變數的作用域延伸了,存在自由變數。主意自由變數如果是不可變型別,需要使用nonlocal
宣告。那麼閉包有什麼用呢?如果要了解Python裝飾器,或許會有所幫助。
參考Luciano Ramalho-《Fluent Python》
相關文章
- 閉包函式(匿名函式)的理解函式
- Swift4.0 sorted(by:)函式理解(閉包$理解)Swift函式
- Python 閉包函式說明Python函式
- python中的閉包函式Python函式
- 函式閉包函式
- 閉包函式函式
- Python基礎之閉包函式Python函式
- go 閉包函式Go函式
- js函式閉包JS函式
- python 關於 函式物件與閉包Python函式物件
- JS學習理解之閉包和高階函式JS函式
- 回撥函式 與 函式閉包函式
- JS閉包函式概念JS函式
- JavaScript 匿名函式 閉包JavaScript函式
- JS函式表示式——函式遞迴、閉包JS函式遞迴
- JS閉包函式和回撥函式JS函式
- 函式物件、裝飾器、閉包函式函式物件
- js函式 函式自呼叫 返回函式的函式 (閉包)JS函式
- python閉包 - 理解與應用Python
- 理解“閉包”
- 理解閉包
- python基礎知識之函式初階——閉包Python函式
- JavaScript4:函式和閉包JavaScript函式
- 3. 匿名函式與閉包函式
- 淺談匿名函式和閉包函式
- 立即執行函式(IIFE)&&閉包函式
- PHP閉包函式使用詳解PHP函式
- 『無為則無心』Python函式 — 35、Python中的閉包Python函式
- 深入理解javascript原型和閉包(2)——函式和物件的關係JavaScript原型函式物件
- Python——五分鐘理解函數語言程式設計與閉包Python函數程式設計
- 理解JavaScript 閉包JavaScript
- Groovy閉包理解
- 理解 JavaScript 閉包JavaScript
- PHP新特性之閉包、匿名函式PHP函式
- 1.13 JavaScript4:函式和閉包JavaScript函式
- PHP 回撥、匿名函式和閉包PHP函式
- 閉包函式,裝飾器詳解函式
- JavaScript進階系列01,函式的宣告,函式引數,函式閉包JavaScript函式