专栏首页Debian中国Python的闭包和装饰器

Python的闭包和装饰器

什么是装饰器?

装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直接看示意图,其中 a 为与装饰器 @a 对应的函数, b 为装饰器修饰的函数,装饰器@a的作用是:

简而言之:@a 就是将 b 传递给 a(),并返回新的 b = a(b)

栗子:

上面使用@dobi来表示装饰器,其等同于:qinfeng = dobi(qinfeng)因此装饰器本质上就是个语法糖,其作用为简化代码,以提高代码可读性,运行上段代码的结果为:

解析过程是这样子的:

1.python 解释器发现@dobi,就去调用与其对应的函数( dobi 函数)

(adsbygoogle = window.adsbygoogle || []).push({});

2.dobi 函数调用前要指定一个参数,传入的就是@dobi下面修饰的函数,也就是 qinfeng()

3.dobi() 函数执行,调用 qinfeng(),qinfeng() 打印“dobi”

什么是闭包?

首先还得从基本概念说起,什么是闭包呢?来看下维基上的解释:

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

….

上面提到了两个关键的地方: 自由变量 和 函数, 这两个关键稍后再说。还是得在赘述下“闭包”的意思,望文知意,可以形象的把它理解为一个封闭的包裹,这个包裹就是一个函数,当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量,自由变量可以在随着包裹到处游荡。当然还得有个前提,这个包裹是被创建出来的。

在通过Python的语言介绍一下,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。

举个例子:

def func(name):
    def inner_func(age):
        print 'name:', name, 'age:', age
    return inner_func

bb = func('the5fire')
bb(26)  # >>> name: the5fire age: 26

这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。

另外再说一点,闭包并不是Python中特有的概念,所有把函数做为一等公民的语言均有闭包的概念。不过像Java这样以class为一等公民的语言中也可以使用闭包,只是它得用类或接口来实现。

nonlocal 语句

在 python 的函数内,可以直接引用外部变量,但不能改写外部变量,因此如果在闭包中直接改写父函数的变量,就会发生错误:

在 python 2 中可以在函数内使用 global 语句,但全局变量在任何语言中都不被提倡,因为它很难控制,python 3 中引入了 nonlocal 语句解决了这个问题:

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

闭包与装饰器

上面已经简单演示了装饰器的功能,事实上,装饰器就是一种的闭包的应用,只不过其传递的是函数:

@makeitalic 装饰器将函数 hello 传递给函数 makeitalic,函数 makeitalic 执行完毕后返回被包装后的 hello 函数,而这个过程其实就是通过闭包实现的。@makebold 也是如此,只不过其传递的是 @makeitalic 装饰过的 hello 函数,因此最后的执行结果<b> 在 <i> 外层,这个功能如果不用装饰器,其实就是显式的使用闭包:

闭包的作用

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

可以看到输出结果是完全一样的,但显然类的实现相对繁琐,且这里只是想输出一下动物的叫声,定义一个 Animal 类未免小题大做,而且 voice 函数在执行完毕后,其作用域就已经释放,但 Animal 类及其实例 dog 的相应属性却一直贮存在内存中:

而这种占用对于实现该功能后,则是没有必要的。除此之外,闭包还有很多其他功能,比如用于封装等,另外,闭包有效的减少了函数参数的数目,这对并行计算非常有价值,比如可以让每台电脑负责一个函数,然后串起来,实现流水化的作业等。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 不与版本帝争,16 年后 SciPy 1.0 版终发布

    SciPy 是一个开源的 Python 算法库和数学工具包。SciPy 包含的模块有最优化、线性代数、积分、插值、特殊函数、快速傅里叶变换、信号处理和图像处理、...

    Debian社区
  • Fedora28正式版发布:GNOME 3.28使用更优雅

    深受Linux之父 — Linus Torvalds 青睐的 GNU/Linux 发行版 – Fedora 于今天发布了 28.0 正式版。Fedora 28 ...

    Debian社区
  • 为 Debian 安装最新版本的 Node.js

    Ubuntu 或 Debian 的软件仓库中的 Node.js 更新较慢,甚至只能等到新版本发布才能有最新的 Node.js 用。下面我们说一下从 NodeSo...

    Debian社区
  • 前端入门19-JavaScript进阶之闭包声明正文-闭包

    作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

    请叫我大苏
  • 视障用户的互联网视界:2018年视障网民移动资讯行为洞察报告

    视障者,广义上个人如果需要透过辅助器具如眼镜、放大镜等才能看清楚东西,就称为视障者。在我国的残障认定标准是两眼中视力较佳的一眼未达0.1或视野各为20度以内者,...

    腾讯大讲堂
  • 视障用户的互联网视界:2018年视障网民移动资讯行为洞察报告

    视障者,广义上个人如果需要透过辅助器具如眼镜、放大镜等才能看清楚东西,就称为视障者。在我国的残障认定标准是两眼中视力较佳的一眼未达0.1或视野各为20度以内者,...

    酷鹅用户研究院
  • 大数据技术分析:HDFS分布式系统介绍!

    Heroop帧的主要特征是通过将数据和计算分布在集群中的各节点服务器来实现分布式计算的目的。在计算逻辑和所需数据接近这一点上,并行计算分区后进行汇总。

    大数据风风
  • SAP ABAP实用技巧介绍系列之 使用simple transformation的mapping功能

    当node VALTO_DATE的value值为20130225,其ABAP类型为D时,输出的xml里的值自动替换成2013.02.25. simple ...

    Jerry Wang
  • 腾讯课堂联合微信搜一搜,助力百大高校开启在线直播云招生

    ? 受疫情防控影响,全国多地取消组织线下高招咨询活动。为使考生和家长更全面、及时地了解各高等院校最新招生政策等报考信息,帮助高校对接有效生源,7月11日起,腾...

    鹅老师
  • 【大牛经验】15款最好用的思维导图工具

    15款最好用的思维导图工具 思维导图也叫心智图,是一项流行的全脑式学习方法,用来表示词,思路,任务或其他与围绕着一个中央关键词或想法项目的示意图。通过径向,图形...

    Java帮帮

扫码关注云+社区

领取腾讯云代金券