闭包
简单点说就是:函数可以访问其函数作用域外部的 局部变量 (嵌套函数引用其封闭范围内的值时,形成了一个闭包)。
它必须满足:
有一个嵌套函数
嵌套函数必须引用封闭函数中定义的值
闭包函数必须返回嵌套函数
它与普通函数的区别在于:内嵌函数使用了外部函数的变量,使外部变量生命周期被延长了(python魔法属性__closure__)。而普通函数的变量在进入其他函数时,变量便无法再用。
这种内部函数调用外部变量的的行为被称为闭包。
def wrapper(param):
def inner(*args, **kwargs):
print('inner', param)
return inner()
wrapper('outer')
输出:inner outer
内部函数inner调用了外部变量param='outer'.当我们在内部返回inner函数对象,这样当外部调用wrapper(‘outer')函数时,实际上只是得到了内部函数对象,而要运行则还需要再加一括号。
转换下:
def wrapper(param):
def inner(*args, **kwargs):
print('inner', param)
return inner
f = wrapper('outer') = inner
f() = wrapper('outer')() = inner(param='outer')
当我们传入的不是普通参数,而是函数时,函数的执行流程就改变了,虽然函数本身的功能并未被改变。
def wrapper(func):
def inner(*args, **kwargs):
print('do sth here')
func()
return inner
从上面可以看到,我们在外部传入的函数调用之前可以做一些其它的事情(print('do sth here'),简单点讲:
不影响原函数的功能,
添加了新的功能
由此引到对闭包的应用:装饰器
装饰器的目的就是:在不影响原来函数功能的基础上,添加新的功能。
装饰器传入的不是普通参数,而是函数。
def wrapper(func):
def inner(*args, **kwargs):
print('do sth here')
func()
return inner
@wrapper
def do_sth():
print('do some function here')
do_sth()
在要调用的函数上通过@wrapper这种方式调用
wrapper在这里就构成了一个装饰器,返回的是内部的inner函数, 而我们传入的参数func,在inner内部执行了,但我们返回的是inner对象,所以,只要我们在外部执行了inner函数,也就执行了内部的func函数。
输出:
do sth here
do some function here
总觉得没有触及本质,需要更新。。。
update:
我们先来看看下面的代码会输出什么? 是andy have 100?
def tell_info(name):
print("%s have %s" %(name, money))
def foo():
money = 100
tell_info("andy")
foo()
答案是:
Traceback (most recent call last):
File "D:/Coding/oldboy/测试.py", line 86, in <module>
foo()
File "D:/Coding/oldboy/测试.py", line 83, in foo
tell_info("andy")
File "D:/Coding/oldboy/测试.py", line 79, in tell_info
print("%s have %s" %(name, money))
NameError: name 'money' is not defined
函数的作用域关系在定义阶段就已经确定,与调用位置无关,无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系,上面的例子中tell_info虽然在foo中调用,但它仍会回到tell_info中查找对应作用域关系,tell_info中没有局部money,也没有全局的变量,所以报错
现在我们回头理解闭包函数中的:内部函数引用了外部函数的变量,使得外部变量的生命周期被了延长 ,可以看到内部函数inner的定义是在外部函数wrapper的内部,也就是说inner的作用域是wrapper的函数内,而wrapper又可以将外部变量传入,所以也就相当于作用域延伸到外部作用域。
相反后面的tell_info函数虽然在foo内部调用,但作用域并不能因些延伸。