Python中的闭包与装饰器是什么
这篇文章将为大家详细讲解有关Python中的闭包与装饰器是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
函数的装饰器可以以某种方式增强函数的功能,如在 Flask 中可使用@app.route('/')为视图函数添加路由,是一种十分强大的功能。在表现形式上,函数装饰器为一种嵌套函数,这其中会涉及到闭包的概念。而在嵌套函数之间,外部函数中的变量相对于内部函数而言为自由变量,使用时可能需要借助于nonlocal关键字进行声明。
nonlocal 声明
按变量的作用域进行分类,Python 中的变量可分为「全局变量」、「局部变量」以及「自由变量」。一般而言,Python 中使用变量前不需要声明变量,但假定在函数体中赋值的变量为局部变量~除非显示使用 global 将在函数中赋值的变量声明为全局变量!
而自由变量则是存在于嵌套函数中的一个概念~定义在其他函数内部的函数被称之为嵌套函数 nested function ,嵌套函数可以访问封闭范围内(外部函数)的变量。嵌套函数不可以在函数外直接访问。
在 Python 中,非本地变量默认仅可读取,在修改时必须显式指出其为非本地变量~自由变量 nonlocal,全局变量 global。
>>>ga=1>>>deffunc():...nb=2...definner():...ga+=1...nb+=2...print('gais%s,andnbis%s'%(ga,nb))...returninner...>>>test=func()Traceback(mostrecentcalllast):...UnboundLocalError:localvariable'ga'referencedbeforeassignment
未加入全局变量和自由变量声明时且使用赋值操作时,inner函数的变量ga, nb默认为局部变量,会报错;如注释掉ga += 1后同样会报错:
Traceback(mostrecentcalllast):...UnboundLocalError:localvariable'nb'referencedbeforeassignment
可行改写如下:
>>>ga=1>>>deffunc():...nb=2...definner():...globalga...nonlocalnb...ga+=1...nb+=2...print('gais%s,andnbis%s'%(ga,nb))...returninner...>>>test=func()>>>test()gais2,andnbis4>>>test()gais3,andnbis6
通过显示声明ga, nb分别为「全局变量」和「自由变量」,此时如预期运行!
闭包
函数内的函数以及其自由变量形成闭包。也即闭包是一种保留定义函数时存在的自由变量的绑定的函数~这样在调用函数时,绑定的自由变量依旧可用。
闭包可以避免全局变量的使用以及提供某种形式的数据隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来进行封装。当变量和函数更加复杂时,则使用类来实现。
#计算移动平均值的函数defmake_averager():series=[]defaverager(new_value):series.append(new_value)total=sum(series)returntotal/len(series)returnaverager
那么此时,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—— 在编译时改变函数功能。
>>>defmake_pretty(func):...definner():...print("Igotdecorated!",end='\t')...func()...returninner...>>>defordinary():...print("Iamordinary!")#用make_pretty函数装饰ordinary函数>>>pretty=make_pretty(ordinary)>>>pretty()Igotdecorated!Iamordinary!
可以作为装饰器的函数内部都有嵌套的功能函数(用以实现主要功能),并返回内部的嵌套函数。
@make_prettydefordinary():print("Iamordinary!")#等价于defordinary():print("Iamordinary!")ordianry=make_pretty(ordinary)
make_pretty(func) 是一个最简单的装饰器,它接受一个函数为其参数;内部定义了一个 inner() 函数~输出 "I got decorated!" 后执行被装饰函数(此时 func 为 inner 闭包中的自由变量);然后返回内部函数 inner。
此时,对于被装饰的函数 ordinary 而言,此时是 inner 的引用:
>>>ordinary()Igotdecorated!Iamordinary!>>>ordinary<functionmake_pretty.<locals>.innerat0x10aeaa1e0>
除了最简单的装饰器之外,还可以将多个装饰器叠放使用
叠放装饰器
defstar(func):definner(*args,**kwargs):print('*'*30)func(*args,**kwargs)print('*'*30)returninnerdefdollar(func):definner(*args,**kwargs):print('$'*30)func(*args,**kwargs)print('$'*30)returninner@star@dollerdefprinter(msg):print(msg)printer("Helloworld!")#结果如下'''******************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Helloworld!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$******************************'''#等价于defprinter(msg):print(msg)printer=star(dollar(printer))
关于Python中的闭包与装饰器是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。