前几天介绍了
Python的装饰器,
提到了函数这个First Clas Object的几个概念,其中闭包函数(Closures)简单说了些,今天再扩展一下有关Closures的内容,以加深对Python装饰器的了解。
什么是闭包(Closures)
怎么定义和使用
嵌套函数中的非局部变量
在了解闭包函数之前,我们首先要了解什么是嵌套函数和非局部变量。
在另一个函数中定义的函数被称为嵌套函数。 嵌套函数可以访问封闭作用域的变量。
在Python中,这些非局部变量默认只能被读取。如果要修改它,我们必须先用“nonlocal”关键字明确声明它是非局部的,才能改变这个变量的赋值。
先看看"nonlocal"的作用是什么?
这个例子中,inner_function()嵌套在outer_function()中。
变量“a”在outer_function()中。所以,如果我们想要在inner_fuction()中修改“a”,我们必须用"nonlocal"声明它是非局部,注意,这里的"a"不是全局变量。
因此我们看到在里面的嵌套函数inner_function()成功修改了外部的变量。
如果不使用关键字"nonlocal",我们得到的结果是这样的:
这里我们没有声明嵌套函数中的变量是非局部的,因此一个新的同名的变量虽然被建立,但是正如我们所看到的,外部的非局部变量"a"却没有被修改。
再来看一个访问非局部变量的嵌套函数的示例。
嵌套函数printer()可以访问外部闭包函数的非局部变量"msg"。
定义闭包函数
从上面的例子中,我们可以看到,嵌套函数可以访问封闭作用域中的非局部变量。
那么,如果函数print_msg()的最后一行返回printe()函数,而不是调用它,又会发生什么呢?我们要重新定义一下函数的功能:
在这里调用函数print_msg()时,附上了参数字符串"Hello",并且将返回的函数赋值给变量"another"。在调用函数another()时,虽然已经完成了函数print_msg()的执行,但这些参数信息一起被记录下来。
数据("Hello")被附加到代码中,这就是Python中的闭包(Closure)。
即使变量超出了作用域,或者函数本身从当前命名空间中移除了,封闭作用域中的这个值也会被记住。
实际操作一下,我们删除函数print_msg,看看会发生什么:
创建闭包函数的条件
从上面的例子可以看出,当一个嵌套函数在其封闭作用域内引用一个值时,我们在Python中就有了一个闭包。
以下几点总结了在Python中创建闭包必须满足的条件。
我们必须有一个嵌套函数(函数内的函数)。
嵌套函数必须引用外部封闭函数中定义的值。
外部封闭函数必须返回嵌套函数。
什么时候用闭包函数?
闭包函数可以避免使用全局值,并且提供某种形式的数据隐藏。 它也可以为这个问题提供一个面向对象的解决方案。
当一个类(Class)中只有很少的方法(method)时(大多数情况下只有一种方法),闭包可以提供一个备用和更优雅的解决方案。 但是,当属性和方法的数量变得很多时,最好还是用类(Class)来实现。
下面是一个简单的例子,闭包可能比定义类和对象更可取。 但是,怎样取决还是看自己的偏好了。
Python中的装饰器广泛地使用着闭包。
最后要说明的是,最好指出闭包函数中包含的值。
所有函数对象都有一个__closure__属性,如果它是一个闭包函数,它将返回一个元组对象。
参考上面的例子,我们知道times3和times5是闭包函数。
用cell_contents可以看到储存的这个值:
以上就是有关闭包的一些内容,弄明白了,对理解装饰器很有帮助。
谢谢阅读,欢迎关注“时光知行”。
领取专属 10元无门槛券
私享最新 技术干货