閉包
在 Python 中,函數(shù)也是一個對象。因此,我們在定義函數(shù)時,可以再嵌套定義一個函數(shù),并將該嵌套函數(shù)返回,比如:
from math import pow def make_pow(n): def inner_func(x): # 嵌套定義了 inner_func return pow(x, n) # 注意這里引用了外部函數(shù)的 n return inner_func # 返回 inner_func
上面的代碼中,函數(shù) make_pow 里面又定義了一個內(nèi)部函數(shù) inner_func,然后將該函數(shù)返回。因此,我們可以使用 make_pow 來生成另一個函數(shù):
>>> pow2 = make_pow(2) # pow2 是一個函數(shù),參數(shù) 2 是一個自由變量 >>> pow2>>> pow2(6) 36.0
我們還注意到,內(nèi)部函數(shù) inner_func 引用了外部函數(shù) make_pow 的自由變量 n,這也就意味著,當(dāng)函數(shù) make_pow 的生命周期結(jié)束之后,n 這個變量依然會保存在 inner_func 中,它被 inner_func 所引用。
>>> del make_pow # 刪除 make_pow >>> pow3 = make_pow(3) Traceback (most recent call last): File "", line 1, in NameError: name "make_pow" is not defined >>> pow2(9) # pow2 仍可正常調(diào)用,自由變量 2 仍保存在 pow2 中 81.0
像上面這種情況,一個函數(shù)返回了一個內(nèi)部函數(shù),該內(nèi)部函數(shù)引用了外部函數(shù)的相關(guān)參數(shù)和變量,我們把該返回的內(nèi)部函數(shù)稱為閉包(Closure)。
在上面的例子中,inner_func 就是一個閉包,它引用了自由變量 n。
閉包的作用閉包的最大特點就是引用了自由變量,即使生成閉包的環(huán)境已經(jīng)釋放,閉包仍然存在;
閉包在運行時可以有多個實例,即使傳入的參數(shù)相同,比如:
>>> pow_a = make_pow(2) >>> pow_b = make_pow(2) >>> pow_a == pow_b False
利用閉包,我們還可以模擬類的實例。
這里構(gòu)造一個類,用于求一個點到另一個點的距離:
from math import sqrt class Point(object): def __init__(self, x, y): self.x, self.y = x, y def get_distance(self, u, v): distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2) return distance >>> pt = Point(7, 2) # 創(chuàng)建一個點 >>> pt.get_distance(10, 6) # 求到另一個點的距離 5.0
用閉包來實現(xiàn):
def point(x, y): def get_distance(u, v): return sqrt((x - u) ** 2 + (y - v) ** 2) return get_distance >>> pt = point(7, 2) >>> pt(10, 6) 5.0
可以看到,結(jié)果是一樣的,但使用閉包實現(xiàn)比使用類更加簡潔。
常見誤區(qū)閉包的概念很簡單,但實現(xiàn)起來卻容易出現(xiàn)一些誤區(qū),比如下面的例子:
def count(): funcs = [] for i in [1, 2, 3]: def f(): return i funcs.append(f) return funcs
在該例子中,我們在每次 for 循環(huán)中創(chuàng)建了一個函數(shù),并將它存到 funcs 中。現(xiàn)在,調(diào)用上面的函數(shù),你可能認(rèn)為返回結(jié)果是 1, 2, 3,事實上卻不是:
>>> f1, f2, f3 = count() >>> f1() 3 >>> f2() 3 >>> f3() 3
為什么呢?原因在于上面的函數(shù) f 引用了變量 i,但函數(shù) f 并非立刻執(zhí)行,當(dāng) for 循環(huán)結(jié)束時,此時變量 i 的值是3,funcs 里面的函數(shù)引用的變量都是 3,最終結(jié)果也就全為 3。
因此,我們應(yīng)盡量避免在閉包中引用循環(huán)變量,或者后續(xù)會發(fā)生變化的變量。
那上面這種情況應(yīng)該怎么解決呢?我們可以再創(chuàng)建一個函數(shù),并將循環(huán)變量的值傳給該函數(shù),如下:
def count(): funcs = [] for i in [1, 2, 3]: def g(param): f = lambda : param # 這里創(chuàng)建了一個匿名函數(shù) return f funcs.append(g(i)) # 將循環(huán)變量的值傳給 g return funcs >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 2 >>> f3() 3小結(jié)
閉包是攜帶自由變量的函數(shù),即使創(chuàng)建閉包的外部函數(shù)的生命周期結(jié)束了,閉包所引用的自由變量仍會存在。
閉包在運行可以有多個實例。
盡量不要在閉包中引用循環(huán)變量,或者后續(xù)會發(fā)生變化的變量。
參考資料本文由 funhacks 發(fā)表于個人博客,采用 Creative Commons BY-NC-ND 4.0(自由轉(zhuǎn)載-保持署名-非商用-禁止演繹)協(xié)議發(fā)布。
非商業(yè)轉(zhuǎn)載請注明作者及出處。商業(yè)轉(zhuǎn)載請聯(lián)系作者本人。
本文標(biāo)題為: Python: 攜帶狀態(tài)的閉包
本文鏈接為: https://funhacks.net/2016/11/...
返回函數(shù) - 廖雪峰的官方網(wǎng)站
Why aren"t python nested functions called closures? - Stack Overflow
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/44262.html
摘要:我們說觸發(fā)了閉包的函數(shù)叫做閉包函數(shù)閉包最大的特點就是它可以被外層函數(shù)返回后賦值給一個變量,并且攜帶了外層函數(shù)內(nèi)定義的變量例子如下變量為函數(shù)開辟的局部命名空間內(nèi)定義的變量函數(shù)內(nèi)引用了變量的內(nèi)層函數(shù)名被當(dāng)作返回值,此時閉包規(guī)則達(dá)成。 什么是閉包? 其實我們在使用函數(shù)過程中不經(jīng)意間就會觸發(fā)閉包,因為總會出于某種原因會在函數(shù)內(nèi)引用或修改上一層函數(shù)的變量,這時就會觸發(fā)閉包 那么什么是閉包?其實就...
摘要:中的函數(shù)本身就是對象,可以攜帶自身狀態(tài),另外還有化等函數(shù)式編程的方法讓函數(shù)緩存狀態(tài),基本上沒有仿函數(shù)存在的必要。 Functor 仿函數(shù)(Functor)是 C++ 里面一個重要的概念,簡而言之就是使用重載了 operator() 運算符的對象模仿函數(shù)的行為,帶來的收益是仿函數(shù)可以攜帶自身狀態(tài),普通的 C++ 函數(shù)不是對象,做不到這一點。 js 中的函數(shù)本身就是對象,可以攜帶自身狀態(tài),...
小編寫這篇文章的主要目的,主要是來給大家介紹,關(guān)于python中,相關(guān)語法問題的解答,比如在python,我們會遇到閉包和裝飾器不會用的情況,那么,下文就會來給大家做一個詳細(xì)的解答。 *args與**kwarsg及閉包和裝飾器 過程 先理解閉包,再理解裝飾器,不要忘了不定長參數(shù) deffunc(): msg='111' deffunc1(): print(ms...
摘要:最近在讀這本評價頗高的語言精粹,其作者是的創(chuàng)造者,在業(yè)界頗有名氣。 最近在讀這本評價頗高的《JavaScript語言精粹》,其作者Douglas Crockford 是JSON的創(chuàng)造者,在業(yè)界頗有名氣。以下是閱讀過程中認(rèn)為比較有用的摘錄的代碼,希望能對各位有所啟發(fā) 自定義的method方法 Function.prototype.method = function(name,func...
摘要:閉包是怎么通過作用域鏈霸占更多內(nèi)存的本文是作者學(xué)習(xí)高級程序設(shè)計第一小節(jié)的一點個人理解,詳細(xì)教程請參考原教材。函數(shù)執(zhí)行過程創(chuàng)建了一個函數(shù)的活動對象,作用域鏈的最前端指向這個對象。函數(shù)執(zhí)行完畢返回值后執(zhí)行環(huán)境作用域鏈和活動對象一并銷毀。 JavaScript 閉包是怎么通過作用域鏈霸占更多內(nèi)存的? 本文是作者學(xué)習(xí)《JavaScript 高級程序設(shè)計》7.2第一小節(jié)的一點個人理解,詳細(xì)教程請...
閱讀 1672·2021-10-29 13:11
閱讀 836·2021-09-22 10:02
閱讀 1696·2021-08-20 09:35
閱讀 1558·2019-08-30 15:54
閱讀 2465·2019-08-30 15:44
閱讀 1389·2019-08-29 16:52
閱讀 1104·2019-08-23 12:56
閱讀 762·2019-08-22 15:16