python闭包和装饰器的区别_深入理解Python中的闭包与装饰器
函数的装饰器可以以某种方式增强函数的功能,如在 Flask 中可使用 @app.route('/') 为视图函数添加路由,是一种十分强大的功能。在表现形式上,函数装饰器为一种嵌套函数,这其中会涉及到闭包的概念。而在嵌套函数之间,外部函数中的变量相对于内部函数而言为自由变量,使用时可能需要借助于 nonlocal 关键字进行声明。
nonlocal 声明
按变量的作用域进行分类,Python 中的变量可分为「全局变量」、「局部变量」以及「自由变量」。一般而言,Python 中使用变量前不需要声明变量,但假定在函数体中赋值的变量为局部变量~除非显示使用 global 将在函数中赋值的变量声明为全局变量!
而自由变量则是存在于嵌套函数中的一个概念~定义在其他函数内部的函数被称之为嵌套函数 nested function ,嵌套函数可以访问封闭范围内(外部函数)的变量。嵌套函数不可以在函数外直接访问。
在 Python 中,非本地变量默认仅可读取,在修改时必须显式指出其为非本地变量~自由变量 nonlocal,全局变量 global。>>> ga = 1
>>> def func():
... nb = 2
... def inner():
... ga = 1
... nb = 2
... print('ga is %s, and nb is %s' % (ga, nb))
... return inner
...
>>> test = func()
Traceback (most recent call last):
...
UnboundLocalError: local variable 'ga' referenced before assignment
未加入全局变量和自由变量声明时且使用赋值操作时,inner 函数的变量 ga, nb 默认为局部变量,会报错;如注释掉 ga = 1 后同样会报错:Traceback (most recent call last):
...
UnboundLocalError: local variable 'nb' referenced before assignment
可行改写如下:>>> ga = 1
>>> def func():
... nb = 2
... def inner():
... global ga
... nonlocal nb
... ga = 1
... nb = 2
... print('ga is %s, and nb is %s' % (ga, nb))
... return inner
...
>>> test = func()
>>> test()
ga is 2, and nb is 4
>>> test()
ga is 3, and nb is 6
通过显示声明 ga, nb 分别为「全局变量」和「自由变量」,此时如预期运行!
闭包
函数内的函数以及其自由变量形成闭包。也即闭包是一种保留定义函数时存在的自由变量的绑定的函数~这样在调用函数时,绑定的自由变量依旧可用。
闭包可以避免全局变量的使用以及提供某种形式的数据隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来进行封装。当变量和函数更加复杂时,则使用类来实现。# 计算移动平均值的函数
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
那么此时,make_averager() 函数的第二行 series = [] 到第七行 return total/len(series) 为闭包,变量 series 为 averager() 函数中的自由变量!# avg 为一个 averager 函数对象 ~ 含自由变量的绑定
>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11
# 创建另一个 averager 函数对象
>>> avg2 = make_averager()
>>> avg2(1)
1.0
>>> avg2(18)
9.5
# 查看 avg, avg2 自由变量中保存的值
>>> avg.__closure__[0].cell_contents
[10, 11, 12]
>>> avg2.__closure__[0].cell_contents
[1, 18]
函数对象通过 __closure__ 属性 —— 返回 cell 对象元祖(函数中有多少嵌套函数则该元祖的长度有多长),生成该对象的函数被称之为闭包函数。
func.__closure__[0].cell_contents: 访问存储在 cell 对象中值。
装饰器
装饰器本身是一个可调用的对象~函数或类,其参数为另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数(如添加一些功能)然后将之返回,或者将之替换为另一个函数或可调用对象。这也被称之为元编程 metaprogramming —— 在编译时改变函数功能。>>> def make_pretty(func):
... def inner():
... print("I got decorated!", end='\t')
... func()
... return inner
...
>>> def ordinary():
... print("I am ordinary!")
# 用 make_pretty 函数装饰 ordinary 函数
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated! I am ordinary!
可以作为装饰器的函数内部都有嵌套的功能函数(用以实现主要功能),并返回内部的嵌套函数。@make_pretty
def ordinary():
print("I am ordinary!")
# 等价于
def ordinary():
print("I am ordinary!")
ordianry = make_pretty(ordinary)
make_pretty(func) 是一个最简单的装饰器,它接受一个函数为其参数;内部定义了一个 inner() 函数~输出 "I got decorated!" 后执行被装饰函数(此时 func 为 inner 闭包中的自由变量);然后返回内部函数 inner。
此时,对于被装饰的函数 ordinary 而言,此时是 inner 的引用:>>> ordinary()
I got decorated! I am ordinary!
>>> ordinary
.inner at 0x10aeaa1e0>
除了最简单的装饰器之外,还可以将多个装饰器叠放使用
叠放装饰器def star(func):
def inner(*args, **kwargs):
print('*' * 30)
func(*args, **kwargs)
print('*' * 30)
return inner
def dollar(func):
def inner(*args, **kwargs):
print('$' * 30)
func(*args, **kwargs)
print('$' * 30)
return inner
@star
@doller
def printer(msg):
print(msg)
printer("Hello world!")
# 结果如下
'''
******************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
Hello world!
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
******************************
'''
# 等价于
def printer(msg):
print(msg)
printer = star(dollar(printer))
python闭包和装饰器的区别_深入理解Python中的闭包与装饰器相关推荐
- python中装饰器的作用_如何理解Python装饰器
展开全部 理解Python中的装饰器 @makebold @makeitalic def say(): return "Hello" 打印出如2113下的输出: Hello 你会怎 ...
- python的上下文管理用哪个关键字_正确理解python中的关键字“with”与上下文管理器...
正确理解python中的关键字"with"与上下文管理器 来源:中文源码网 浏览: 次 日期:2018年9月2日 [下载文档: 正确理解python中的关键字&quo ...
- python函数可以作为容器对象吗_正确理解Python函数是第一类对象
正确理解 Python函数,能够帮助我们更好地理解 Python 装饰器.匿名函数(lambda).函数式编程等高阶技术. 函数(Function)作为程序语言中不可或缺的一部分,太稀松平常了.但函数 ...
- python for和while的用法区别_详解python while 函数及while和for的区别
1.while循环(只有在条件表达式成立的时候才会进入while循环) while 条件表达式: pass while 条件表达式: pass else: pass 不知道循环次数,但确定循环条件的时 ...
- python不同的版本有什么区别_多版本python的区别方法
最近做在线教育平台的过程中,由于开始使用的是最新的pyhon3.6版本,在后面需要用到xadmin管理后台(django本身的admin也可以,由于跟着的视频课程使用了xadmin,担心后面会有坑自己 ...
- 用python画爱心再加一行文字_如何理解python一行代码实现一个爱心字符画?
前言 python中有个很酷的效果,一行代码实现一个爱心字符,虽说是一行代码,但是理解起来还是比较难的,括号太多,并且使用了python的一些快捷小技巧.比如三元表达式,列表生成式,字符串拼接以及一个 ...
- 从编译器层面理解C#中的闭包的这个坑!
前言 在公众号上看到一篇文章<正确使用和理解C#中的闭包>,里面提到了闭包的一个坑: 当捕获的外部变量为for循环的迭代变量时,C#认为变量i是定义在循环体外的.所以,当添加委托集合的fo ...
- python装饰器的通俗理解_简单理解Python装饰器
Python有大量强大又贴心的特性,如果要列个最受欢迎排行榜,那么装饰器绝对会在其中. 刚接触装饰器,会觉得代码不多却难以理解.其实装饰器的语法本身挺简单的,复杂是因为同时混杂了其它的概念.下面我们一 ...
- python装饰器由浅入深_由浅入深理解Python装饰器
前提知识: 1.Python里函数也是一种对象: def shout(word="yes"): return word.capitalize()+"!" pri ...
最新文章
- m1芯片 服务器,m1服务器地址
- mysql 权重搜索
- ubuntu/debian/centos/rhel使用镜像源一键安装gitlab-ce服务
- mysql safe无法启动_(转)mysqld_safe无法启动的解决办法
- UI5 Control lazy load mechanism
- 运动合成——机器学习技术
- centos7:塔建pure_ftpd虚拟用户
- Redis禁用危险命令
- arcgis server mysql_ArcGIS Server 10.1发布要素服务时遇到的数据库注册问题总结 (二)...
- Java中Link,set 和Map的区别,ArrayList,HashSet和HashMap的区别。
- MySQL数据库通过cmd窗口导入sql文件
- Spectrum采集卡在飞行时间质谱中的应用案例
- aspack脱壳脚本的编写
- 家用电脑如何安装服务器系统,普通电脑安装服务器系统
- 14.如何在Linux电脑中使用终端运行INSTALL.sh文件
- 香港服务器要个人信息么,香港个人信息应当遵循服务器23.225合法
- PS:换背景天空(简单抠图)
- 怎么截取巨大日志里某一天的log
- excel里的一个单元格怎样拆分成几个单元格?
- 16、什么是拟牛顿法(Quasi-Newton Methods)?