閉包
所謂閉包掏秩,就是將組成函數(shù)的語句和這些語句的執(zhí)行環(huán)境打包在一起時(shí),得到的對(duì)象。
- 栗子1
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f) #這里的 f 已經(jīng)是一個(gè)函數(shù) 加入了
return fs
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
上面這個(gè)函數(shù)有點(diǎn)糾結(jié)档插!
糾結(jié)點(diǎn)1:f1, f2, f3 = count()是什么意思沙绝?
答:f1, f2, f3 = count() 這個(gè)并不是我猜想3個(gè)都平等同樣的指向fs這個(gè)list,更有可能的是搏明,這個(gè)三個(gè)這是分別指向了list中的3個(gè)元素
(1)只有元素才有可能是int
(2)如果只寫成f1,f2 = count()
的話
會(huì)報(bào)錯(cuò)ValueError: too many values to unpack (expected 2)
寫成f1,f2,f3,f4= count()
的話
會(huì)報(bào)錯(cuò)ValueError: need more than 3 values to unpack
(3)打印出來看
>>>print(f1,f2,f3)
返回3個(gè)<function count.<locals>.f at 0x00000000037F5400>
>>>f1= count
>>>print(f1())
[<function count.<locals>.f at 0x0000000002F65268>,
<function count.<locals>.f at 0x0000000002F65400>,
<function count.<locals>.f at 0x0000000002F65488>]
說明,如果返回一個(gè)列表闪檬,那么這個(gè)列表每個(gè)元素都是函數(shù)熏瞄。
糾結(jié)點(diǎn)2:為什么f1(),f2(),f3()都返回9,即為啥列表中的元素都是9谬以?
原因是返回函數(shù)引用了變量i强饮,下面來解析下f1,f2,f3=count()
這句的執(zhí)行過程:
當(dāng)i=1, 執(zhí)行for循環(huán), 結(jié)果返回函數(shù)f的函數(shù)地址为黎,存在列表fs中的第一個(gè)位置上邮丰。
當(dāng)i=2, 由于fs列表中第一個(gè)元素所指的函數(shù)中的i是count函數(shù)的局部變量行您,i也指向了2;然后執(zhí)行for循環(huán)剪廉, 結(jié)果返回函數(shù)f的函數(shù)地址娃循,存在列表fs中的第二個(gè)位置上。
當(dāng)i=3, 同理斗蒋,在fs列表第一個(gè)和第二個(gè)元素所指的函數(shù)中的i變量指向了3捌斧; 然后執(zhí)行for循環(huán), 結(jié)果返回函數(shù)f的函數(shù)地址泉沾,存在列表fs中的第三個(gè)位置上捞蚂。
所以在調(diào)用f1()的時(shí)候,函數(shù)中的i是指向3的:
f1():
return 3*3
同理f2(), f3()結(jié)果都為9
注意:返回閉包時(shí)牢記的一點(diǎn)就是:返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量跷究。
- 栗子2:如果一定要引用循環(huán)變量怎么辦姓迅?方法是再創(chuàng)建一個(gè)函數(shù),用該函數(shù)的參數(shù)綁定循環(huán)變量當(dāng)前的值俊马,無論該循環(huán)變量后續(xù)如何更改丁存,已綁定到函數(shù)參數(shù)的值不變:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執(zhí)行,因此i的當(dāng)前值被傳入f()
return fs
注意:當(dāng)i=1時(shí)柴我,f(1)即讓j指向1解寝,f(1)立即被執(zhí)行,返回值是g函數(shù)艘儒,g函數(shù)作為參數(shù)傳入fs.append()里
當(dāng)i=2時(shí)编丘,f(2)即讓j指向2,此時(shí)j不是count的局部變量彤悔,不會(huì)影響到i=1是f(1)中j的指向嘉抓。即函數(shù)f的參數(shù)綁定循環(huán)變量當(dāng)前的值, 而不是循環(huán)變量本身。
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9