在提到閉包
之前,我們需要對(duì)函數(shù)做一些梳理:
函數(shù)
的局部參數(shù)是無法保存的挽铁,每次執(zhí)行函數(shù)都是將參數(shù)初始化并執(zhí)行
而閉包
可以使函數(shù)擁有自己的環(huán)境上下文
,在其中保存執(zhí)行后的信息伟桅。使函數(shù)表現(xiàn)的像一個(gè)對(duì)象
,豐富了函數(shù)的功能叽掘。
0楣铁、引言
我們現(xiàn)在要實(shí)現(xiàn)一個(gè)具有緩存功能的函數(shù),"如果后續(xù)傳入相同的參數(shù)够掠,則不再計(jì)算民褂,直接返回結(jié)果"
。
如果是在對(duì)象中,這種緩存非常簡單就能實(shí)現(xiàn)赊堪。但是在函數(shù)中則異常麻煩:
- 1
幻想中的寫法
, [實(shí)際上并不能起到作用]
import time
def cache_sum(a, b):
"""這是一個(gè)具有緩存功能的求和函數(shù)"""
cache = {}
key = str(a) + str(b)
if key in cache: # 1. 具有緩存結(jié)果面殖,則直接返回
return cache.get(key)
else:
# 2. 計(jì)算
time.sleep(1)
ret = a + b
cache[key] = ret # 3. 將結(jié)果存儲(chǔ)進(jìn)緩存
使用cache_sum
if __name__ == '__main__':
print(datetime.datetime.now()) # 2020-05-04 09:13:13.239913
cache_sum(1, 5)
print(datetime.datetime.now()) # 2020-05-04 09:13:14.242900
cache_sum(1, 5)
print(datetime.datetime.now()) # 2020-05-04 09:13:15.246474
可以看出,在函數(shù)中cache = {}
作為局部變量哭廉,每次都會(huì)被初始化脊僚,根本起不到作用 [ 兩次執(zhí)行都耗時(shí)一秒,說明緩存沒有起作用
]。
改進(jìn)寫法
def cache_sum(a, b):
"""這是一個(gè)具有緩存功能的求和函數(shù)"""
key = str(a) + str(b)
if key in cache: # 1. 這里的 cache 由使用者提供
return cache.get(key)
else:
# 2. 計(jì)算
time.sleep(1)
ret = a + b
cache[key] = ret # 3. 將結(jié)果存儲(chǔ)進(jìn)緩存
使用 cache_sum
if __name__ == '__main__':
cache = {}
print(datetime.datetime.now()) # 2020-05-04 09:19:43.842472
cache_sum(1, 5)
print(datetime.datetime.now()) # 2020-05-04 09:19:44.846783
cache_sum(1, 5)
print(datetime.datetime.now()) # 2020-05-04 09:19:44.846864
[ 第二次沒有耗時(shí),說明緩存起作用了
]
在這種寫法中遵绰,我們執(zhí)行cache_sum
還需要提供 cache = {}
這樣一個(gè)變量辽幌,這樣會(huì)引起很多問題:
使用者會(huì)不會(huì)忘記提供 cache = {}?
使用者會(huì)不會(huì)在外部修改 cache?
這些都是無法預(yù)料的問題椿访。顯然不能作為這個(gè)問題的解決方式- 使用
閉包
完成
- 使用
import time
def as_cache_sum():
"""返回一個(gè)具有緩存功能的函數(shù)
你應(yīng)該這樣使用它
cache_sum = as_cache_sum()
ret = cache_sum(1,4)
print(ret) # 5
"""
cache = {}
def cache_sum(a, b):
key = str(a) + str(b)
if key in cache: # 1. 具有緩存結(jié)果乌企,則直接返回
return cache.get(key)
else:
# 2. 計(jì)算
time.sleep(1)
ret = a + b
cache[key] = ret # 3. 將結(jié)果存儲(chǔ)進(jìn)緩存
return cache_sum
與第二種
寫法相比,這里提供變量cache
是指外部函數(shù)中完成的成玫,而使用者是接觸不到這個(gè)變量的加酵。
細(xì)細(xì)的品,你是不是有點(diǎn)明白
閉包
的這個(gè)閉
了
一哭当、使用“閉包”
“閉包”
的本質(zhì)是函數(shù)的嵌套定義猪腕,即在函數(shù)內(nèi)部再定義函數(shù)。
在下面這個(gè)函數(shù)中
def make_averager():
"""返回一個(gè)計(jì)算平均值的函數(shù)"""
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series)
return averager
調(diào)用 make_averager
時(shí)钦勘,返回一個(gè) averager
函數(shù)對(duì)象陋葡。每次調(diào)用 averager
時(shí),它會(huì) 把參數(shù)添加到系列值中彻采,然后計(jì)算當(dāng)前平均值腐缤。
averager = make_averager()
print(averager(10)) # 10
print(averager(12)) # 11
print(averager(14)) # 12
在上述函數(shù)中make_averager
并不包含真正的執(zhí)行邏輯,它只做了兩件事情
為真正的執(zhí)行函數(shù)提供環(huán)境上下文
返回執(zhí)行函數(shù)
averager
函數(shù)才是真正執(zhí)行邏輯的地方肛响,它使用了make_averager
為它提供了series
環(huán)境變量柴梆,averager
對(duì)series
的修改會(huì)被保存起來(伴隨著averager
,直到其被銷毀)