在提到闭包
之前,我们需要对函数做一些梳理:
函数
的局部参数是无法保存的,每次执行函数都是将参数初始化并执行
而闭包
可以使函数拥有自己的环境上下文
,在其中保存执行后的信息。使函数表现的像一个对象
,丰富了函数的功能。
0、引言
我们现在要实现一个具有缓存功能的函数,"如果后续传入相同的参数,则不再计算,直接返回结果"
。
如果是在对象中,这种缓存非常简单就能实现。但是在函数中则异常麻烦:
- 1
幻想中的写法
, [实际上并不能起到作用]
import time
def cache_sum(a, b):
"""这是一个具有缓存功能的求和函数"""
cache = {}
key = str(a) + str(b)
if key in cache: # 1. 具有缓存结果,则直接返回
return cache.get(key)
else:
# 2. 计算
time.sleep(1)
ret = a + b
cache[key] = ret # 3. 将结果存储进缓存
使用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
可以看出,在函数中cache = {}
作为局部变量,每次都会被初始化,根本起不到作用 [ 两次执行都耗时一秒,说明缓存没有起作用
]。
改进写法
def cache_sum(a, b):
"""这是一个具有缓存功能的求和函数"""
key = str(a) + str(b)
if key in cache: # 1. 这里的 cache 由使用者提供
return cache.get(key)
else:
# 2. 计算
time.sleep(1)
ret = a + b
cache[key] = ret # 3. 将结果存储进缓存
使用 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
[ 第二次没有耗时,说明缓存起作用了
]
在这种写法中,我们执行cache_sum
还需要提供 cache = {}
这样一个变量,这样会引起很多问题:
-
使用者会不会忘记提供 cache = {}?
-
使用者会不会在外部修改 cache?
这些都是无法预料的问题。显然不能作为这个问题的解决方式 - 使用
闭包
完成
- 使用
import time
def as_cache_sum():
"""返回一个具有缓存功能的函数
你应该这样使用它
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. 具有缓存结果,则直接返回
return cache.get(key)
else:
# 2. 计算
time.sleep(1)
ret = a + b
cache[key] = ret # 3. 将结果存储进缓存
return cache_sum
与第二种
写法相比,这里提供变量cache
是指外部函数中完成的,而使用者是接触不到这个变量的。
细细的品,你是不是有点明白
闭包
的这个闭
了
一、使用“闭包”
“闭包”
的本质是函数的嵌套定义,即在函数内部再定义函数。
在下面这个函数中
def make_averager():
"""返回一个计算平均值的函数"""
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series)
return averager
调用 make_averager
时,返回一个 averager
函数对象。每次调用 averager
时,它会 把参数添加到系列值中,然后计算当前平均值。
averager = make_averager()
print(averager(10)) # 10
print(averager(12)) # 11
print(averager(14)) # 12
在上述函数中make_averager
并不包含真正的执行逻辑,它只做了两件事情
为真正的执行函数提供环境上下文
返回执行函数
averager
函数才是真正执行逻辑的地方,它使用了make_averager
为它提供了series
环境变量,averager
对series
的修改会被保存起来(伴随着averager
,直到其被销毁)
网友评论