闭包
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
f1()和f2()的调用结果互不影响。
练习
利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter():
def counter():
return 1
return counter
测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
print('测试通过!')
else:
print('测试失败!')
答案
def createCounter():
n = 0
def counter():
nonlocal n
n += 1
return n
return counter
def createCounter():
f = [0]
def counter():
f[0] += 1
return f[0]
return counter
解释
nonlocal关键字:用来告诉内部函数,后边跟的变量需要到上一级函数里边去找,什么时候用?闭包内部函数需要修改外部函数的临时变量的时候
做一个类比就好理解了:
一个函数在函数内部要访问全局变量,当然可以,但是要修改全局变量(个人认为修改变量无外乎两种情况,可变对象原地修改;不可变对象新建一个对象,然后把变量指向新对象),很抱歉不可以。那用什么方法呢?
解决方法:a.加上global关键字 b.全局变量用可变对象,比如列表
同样的,闭包方便内部函数去获取外部函数的临时变量,但是不允许对其修改,要修改怎么办?
a.加nonlocal关键字声明一下 b.要修改的对象用可变对象储存!
这就是为什么有的答案用list有的答案用nonlocal
匿名函数
匿名函数lambda x: x * x实际上就是:
def f(x):
return x * x
关键字lambda
表示匿名函数,冒号前面的x
表示函数参数。
装饰器
本质上,decorator就是一个返回函数的高阶函数。
以这道练习为例
请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
代码如下
import time, functools
def metric(fn):
@functools.wraps(fn)
def wrapper(*args, **kw):
t_start = time.time()
result = fn(*args, **kw)
t_end = time.time()
print('%s executed in %s ms' % (fn.__name__, t_end-t_start))
return result
return wrapper
可以看到wrapper里面先记录开始的时间,再调用原来的fn函数,最后记录结束的时间,用结束时间减去开始时间即运行时间
注意decorator返回的值是一个函数,若函数名为fast,则
@metric
def fast()
等价于
fast=metric(fast)
这样就做到了在不改变原函数结构的情况下添加了新的功能