前提知识(如果你不知道可能需要去百度):

  1. Python中一切皆为对象!
  2. 函数也是对象,它可以像其他对象一样存储在数据结构中(例如列表)
  3. 函数可以作为参数传递给另一个函数,也可以作为结果被一个函数返回。

什么是闭包?

关于闭包是什么,维基百科是这么解释的:

在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
….

在Python中,闭包的通俗解释就是:

一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。自由变量很“自由”,它可以被内部函数B调用。

栗子:

1
2
3
4
5
6
7
8
>>> def A(a):
... def B(b):
... print(a+b)
... return B
...
>>> test=A('Hello')
>>> test('World')
HelloWorld

子函数修改父函数的变量的方法——nonlocal语句

我们现在把上文中介绍的外部函数A称为父函数,内部函数B称为子函数。

我们都知道Python函数内,可以直接引用外部变量,但不能改写外部变量,如果改写将会发送错误,那如果我们需要改写呢?有什么解决办法?

一个办法是使用global语句,但这种方法不被提倡,因为它不好控制。

另一个办法就是nonlocal语句。

再看栗子:

1
2
3
4
5
6
7
8
9
10
11
>>> def A(a):
... wu='5555'
... def B(b):
... nonlocal wu
... wu='23333'
... print(a+b+wu)
... return B
...
>>> test=A('Hello')
>>> test('World')
HelloWorld23333

Nonlocal 与 global 的区别在于 nonlocal 语句会去搜寻本地变量与全局变量之间的变量,其会优先寻找层级关系与闭包作用域最近的外部变量。

**
**

装饰器

装饰器是闭包的一种应用,它接收函数作为参数,返回包装好了的函数。

常用于:

  • 日志(logging)
  • 访问控制和授权
  • 衡量函数,如执行时间
  • 限制请求速率
  • 缓存,等等….

一个典型栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> def strong(func):
... def wrapper():
... return '<strong>'+func()+'</strong>'
... return wrapper
...
>>> def emphasis(func):
... def wrapper():
... return '<em>'+func()+'</em>'
... return wrapper
...
>>> @strong
... @emphasis
... def greet():
... return "Hello!"
...
>>> greet()
'<strong><em>Hello!</em></strong>'
>>>

这个例子摘自《深入理解Python特性》

从例子中我们可以看出一种简单的装饰器写法。

另一个值得注意的地方是装饰器的装饰顺序:

从下往上装饰,靠近函数的先装饰(例子中emphasis更靠近greet)。

闭包的作用:

闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在,这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。

通俗的说就是:在利用内部函数(闭包)引用的资源不会被回收这一特性!