Python 支持函數(shù)式編程关带,所以存在閉包,閉包是由函數(shù)及其相關的引?環(huán)境組合?成的實體 沼撕, ?句話: 閉包 = 函數(shù)+引?環(huán)境,函數(shù)式編程中芜飘,當內嵌函數(shù)體內引?到體外的變量 時务豺, 將會連同這些變量(引?環(huán)境)和內嵌函數(shù)體, ?塊打包成?個整體返回
如果在一個函數(shù)的內部定義了另一個函數(shù)嗦明,外部的我們叫它為外函數(shù)笼沥,內部的我們叫它內函數(shù),那么閉包就是在一個外函數(shù)中定義了一個內函數(shù)娶牌,內函數(shù)里運用了外函數(shù)的臨時變量奔浅,并且外函數(shù)的返回值是內函數(shù)的引用。
一般情況下诗良,在我們認知當中汹桦,如果一個函數(shù)結束,函數(shù)的內部所有東西都會釋放掉鉴裹,還給內存舞骆,局部變量都會消失钥弯。但是閉包是一種特殊情況,如果外函數(shù)在結束的時候發(fā)現(xiàn)有自己的臨時變量將來會在內部函數(shù)中用到督禽,就把這個臨時變量綁定給了內部函數(shù)脆霎,然后自己再結束。
簡單示例:
#閉包函數(shù)的實例
# outer是外部函數(shù) a和b都是外函數(shù)的臨時變量
def outer( a ):
? ? b = 10
? ? # inner是內函數(shù)
? ? def inner():
? ? ? ? #在內函數(shù)中 用到了外函數(shù)的臨時變量
? ? ? ? print(a+b)
? ? # 外函數(shù)的返回值是內函數(shù)的引用
? ? return inner
if __name__ == '__main__':
? ? # 在這里我們調用外函數(shù)傳入?yún)?shù)5
? ? #此時外函數(shù)兩個臨時變量 a是5 b是10 狈惫,并創(chuàng)建了內函數(shù)睛蛛,然后把內函數(shù)的引用返回存給了demo
? ? # 外函數(shù)結束的時候發(fā)現(xiàn)內部函數(shù)將會用到自己的臨時變量,這兩個臨時變量就不會釋放胧谈,會綁定給這個內部函數(shù)
? ? demo = outer(5)
? ? # 我們調用內部函數(shù)忆肾,看一看內部函數(shù)是不是能使用外部函數(shù)的臨時變量
? ? # demo存了外函數(shù)的返回值,也就是inner函數(shù)的引用第岖,這里相當于執(zhí)行inner函數(shù)
? ? demo() # 15
? ? demo2 = outer(7)
? ? demo2()#17
(1)外函數(shù)返回了內函數(shù)的引用:
在python中一切都是對象难菌,包括整型數(shù)據(jù)1,函數(shù)蔑滓,其實是對象郊酒,當我們進行a=1的時候,實際上在內存當中有一個地方存了值1键袱,然后用a這個變量名存了1所在內存位置的引用燎窘。引用就好像c語言里的指針,大家可以把引用理解成地址蹄咖。a只不過是一個變量名字褐健,a里面存的是1這個數(shù)值所在的地址,就是a里面存了數(shù)值1的引用澜汤。
相同的道理蚜迅,當我們在python中定義一個函數(shù)def demo(): 的時候,內存當中會開辟一些空間俊抵,存下這個函數(shù)的代碼谁不、內部的局部變量等等。這個demo只不過是一個變量名字徽诲,它里面存了這個函數(shù)所在位置的引用而已刹帕。我們還可以進行x = demo, y = demo谎替, 這樣的操作就相當于偷溺,把demo里存的東西賦值給x和y,這樣x 和y 都指向了demo函數(shù)所在的引用钱贯,在這之后我們可以用x() 或者 y() 來調用我們自己創(chuàng)建的demo() 挫掏,調用的實際上根本就是一個函數(shù),x喷舀、y和demo三個變量名存了同一個函數(shù)的引用砍濒。
有了上面的解釋淋肾,我們可以繼續(xù)說,返回內函數(shù)的引用是怎么回事了爸邢。對于閉包樊卓,在外函數(shù)outer中 最后return inner,我們在調用外函數(shù) demo = outer() 的時候杠河,outer返回了inner碌尔,inner是一個函數(shù)的引用,這個引用被存入了demo中券敌。所以接下來我們再進行demo() 的時候唾戚,相當于運行了inner函數(shù)。
同時我們發(fā)現(xiàn)待诅,一個函數(shù)叹坦,如果函數(shù)名后緊跟一對括號,相當于現(xiàn)在我就要調用這個函數(shù)卑雁,如果不跟括號募书,相當于只是一個函數(shù)的名字,里面存了函數(shù)所在位置的引用测蹲。
在閉包內函數(shù)中莹捡,我們可以隨意使用外函數(shù)綁定來的臨時變量,但是如果我們想修改外函數(shù)臨時變量數(shù)值的時候發(fā)現(xiàn)出問題了
在基本的python語法當中扣甲,一個函數(shù)可以隨意讀取全局數(shù)據(jù)篮赢,但是要修改全局數(shù)據(jù)的時候有兩種方法:1 global 聲明全局變量 2 全局變量是可變類型數(shù)據(jù)的時候可以修改
def lazy_sum(*args):
? ? def sum():
? ? ? ? x=0fornin args:
? ? ? ? ? ? x=x+n
? ? ? ? return x
? ? return sum
lazy_sum(1,2,3,4,5,6,7,8,9) #這時候lazy_sum 并沒有執(zhí)行,而是返回一個指向求和的函數(shù)的函數(shù)名sum 的內存地址琉挖。
但是如果? ?上面函數(shù)是return sum() 那么? ??lazy_sum(1,2,3,4,5,6,7,8,9) 就執(zhí)行了
f=lazy_sum(1,2,3,4,5,6,7,8,9)
print(type(f))
print(f())? # 調用f()函數(shù)启泣,才真正調用了 sum 函數(shù)進行求和,
import time
def? run_time(c):
def run_t(*args,**kwargs ):
a=time.time()
ret=c(*args,**kwargs )
print('內函數(shù)里的第一句------------1-----------')
time.sleep(2)
b=time.time()
print(float(b-a))
print('內函數(shù)的第二句')
return ret
return run_t
@run_time
def? foo(d,e):
print('3')
print(d+e)
return ('foo的return')
t=foo(15,15)
print(t)
#做個小筆記 carl王? ? 6.8cnstrong
# 裝飾器的使用是2個函數(shù)的嵌套示辈, 一個函數(shù)里去定義另一個函數(shù)种远,然后外函數(shù)的參數(shù)里是用函數(shù)來做形參,那么就可以在這個函數(shù)外面去@外函數(shù)做一個裝飾器顽耳,此時你在定義一個第三個函數(shù),該函數(shù)就可以作為上述的1個嵌套函數(shù)的補充妙同,這個函數(shù)也就是嵌套函數(shù)里的第一個函數(shù)的形參位置放入射富,然后內部函數(shù)可以去執(zhí)行這個第三個函數(shù)(前提是有return到這個內函數(shù)),然后內函數(shù)里可以加入*和**粥帚,那么第三個函數(shù)的數(shù)據(jù)也會同時參與到這個內函數(shù)中胰耗,同時內函數(shù)的操作也會執(zhí)行,還有一個點就是return芒涡,可以在內函數(shù)中給一個變量=形參柴灯,然后在里面把內函數(shù)return這個變量出來卖漫,在第三個函數(shù)的定義中也加上return,那么最后如果你要print第三個函數(shù)赠群,就是打印了第三個函數(shù)里面的return