Python 闭包

闭包

简单点说就是:函数可以访问其函数作用域外部的  局部变量 (嵌套函数引用其封闭范围内的值时,形成了一个闭包)。

它必须满足:

有一个嵌套函数

嵌套函数必须引用封闭函数中定义的值

闭包函数必须返回嵌套函数

它与普通函数的区别在于:内嵌函数使用了外部函数的变量,使外部变量生命周期被延长了(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内部调用,但作用域并不能因些延伸。

上一篇:标签绑定事件的几种方式

下一篇:Ajax发送多选框的值的bug