学习
实践
活动
专区
工具
TVP
写文章

理解Python装饰器,要先弄清闭包函数

前几天介绍了

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可以看到储存的这个值:

以上就是有关闭包的一些内容,弄明白了,对理解装饰器很有帮助。

谢谢阅读,欢迎关注“时光知行”。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180214A12V1600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

关注

腾讯云开发者公众号
10元无门槛代金券
洞察腾讯核心技术
剖析业界实践案例
腾讯云开发者公众号二维码

扫码关注腾讯云开发者

领取腾讯云代金券