python闭包和装饰器的关系_python闭包和装饰器

本文目录:

1. 闭包的解析和用法

2. 函数式装饰器

3. 类装饰器

一、闭包

闭包是一种函数,从形式上来说是函数内部定义(嵌套)函数,实现函数的扩展。在开发过程中,考虑到兼容性和耦合度问题,如果想在原有的函数基础上添加东西而又不改动原有函数的结构,通常会使用闭包。但闭包的功能还不只是这个。实际上,闭包会保留定义函数时存在的自由变量的绑定,这样在调用函数时,虽然定义作用域不可用了,但是仍然可以使用那些绑定的变量。简单来说,普通函数在调用完后,函数内部的变量就释放了(因为直接调用的函数没有绑定在某一个对象上,Cpython解析器就会把它回收了),而闭包内部的变量仍然保存着。

普通函数不会保存变量的值:

例如:deffunction(value):

nums=[]

nums.append(value)returnnums

func= function(2)

func2= function(3)print(func) #[2]

print(func2) #[3]

两次调用函数返回的值都是不相同的

在闭包中,外部函数的变量一直会为内部函数“保留”着,每次调用函数都可以获取这些变量

defclosure():

nums=[]deffunction(value):

nums.append(value)returnnumsreturnfunction

close=closure()

close(5)

close(6)

close(7)#元组形式返回嵌套函数function的变量

print(close.__code__.co_varnames) #('value',)

print(close.__code__.co_freevars) #('nums',)#列表形式保存嵌套函数中自由变量的值

print(close.__closure__[0].cell_contents) #[5, 6, 7]

闭包的执行顺序可以理解为:

closure(function(value))

因此close = closure()相当于为闭包创建了一个绑定的对象,这个对象内部有变量nums和函数function。当变量在下一次调用的时候,这些量还会保存着。因此当多次调用close()的时候,nums列表会更新。

以上代码的解析:

对象审查(反射)

_ _code_ _返回对象中的函数

_ _co_varnames 返回内部函数中保存的变量,例如function中的value

_ _co_freenames 返回内部函数中的自由变量,自由变量是编程中的一个专业名词。

如上面的代码中,nums并不是在function函数中绑定的,它是在它的外部函数的作用域范围内绑定的,所以在function内部,nums是一个自由变量。而close对象为function函数保留了这个自由变量,在每次调用函数时,都可以更新这个自由变量。

自由变量(所有的)实际保存在闭包中,可以通过_ _closure_ _来获取,它是一个列表,每个元素都表示一个自由变量,如上面的nums。每个元素都是一个cell,它的属性cell_contents保存着这个自由变量的值,因此有:

_ _closure_ _[0].cell_contents

更多关于函数/类/生成器的审查可以参考官方文档:

二、函数形式的装饰器

上面讲了如何通过嵌套函数实现一个闭包,下面将装饰器是如何实现的。实际上,装饰器离不开对闭包的理解,函数形式的装饰器看起来像是闭包换了一种表达形式,调用起来更灵活和更方便。

例如:

#函数形式的闭包

registry =[]defdecorator(func):print('registe %s'%func)

registry.append(func)return func #返回的量必须是一个函数,否则会报错

@decoratordeffun1():print('running fun1')

@decoratordeffun2():print('running fun2')

@decoratordeffun3():print('running fun3')

fun1()

fun2()

fun3()

结果:

registe registeregisterunning fun1

running fun2

running fun3

装饰器看起来有点像闭包,只不过是加了一个@的外壳,而这个外壳函数的参数必须是一个函数,并且必须要有返回函数(返回的一般是内部函数)

装饰器的执行顺序:

decorator(func)

内部函数的参数可以在函数调用时传入,而不必像闭包那样必须由对象传入。

值得注意的是:装饰器函数有导入时运行和运行时运行的区别,装饰器在模块导入的时候就执行了,而被装饰的函数则在调用的时候才执行。

上面这个被装饰器“装饰”的函数似乎看起来跟装饰器“互动”很少,那么下面结合装饰器和闭包实现一个更复杂的装饰器:

defdecorator(func):def outerFunc(*args): #装饰器内部实现闭包,闭包的外部函数接受任意定位变量

outerFunName = outerFunc.__name__innerFunName= func.__name__

print('change innerFunc:%s to outerFunc:%s'%(innerFunName, outerFunName))

result= func(*args) #可以实现,因为闭包中保存了自由变量func

result += "and start running"

returnresultreturn outerFunc #将改变返回的函数,返回外部函数

@decoratordeffun(str):returnstr

str= fun('This is funciton1') #change innerFunc:fun to outerFunc:outerFunc

print(str) #This is funciton1 and start running

装饰器执行顺序:

decorator(outerFunc(func(args)))

这个装饰器内部的闭包实现还是比较简单的,只是为了说明原理,在编程过程中可以根据实际添加更多的功能实现。

继续改造,让装饰器也带上参数:

#带参数装饰器

defdecorator(name):def_decorator(func):def outerFunc(*args):print(name)

outerFunName= outerFunc.__name__innerFunName= func.__name__

print('change innerFunc:%s to outerFunc:%s'%(innerFunName, outerFunName))

result= func(*args)

result+= "and start running"

returnresultreturnouterFuncreturn_decorator

@decorator(name='@Author:Tom')deffun(str):returnstr

str= fun('This is funciton1')print(str)

结果:

@Author:Tom

change innerFunc:fun to outerFunc:outerFunc

Thisis funciton1 and start running

三、类形式的装饰器

讲完了函数形式的装饰器,那么接下来讲讲类形式的装饰器

一般类的定义如下:

classTest(object):def __init__(self,name):

self._m=0

self._n=0

self.name=namedeftest_print(self):print(self.name)

而如果想要将一个类变成一个装饰器,那么就需要一个很关键的魔法方法_ _call_ _(),它的作用是将一个类实例变成可调用的,改造一下上面的类:

classTest(object):def __init__(self):

self.count=0def __call__(self):#print(self.count)

self.count += 1 #每一次调用这个类实例都记录一次

returnself.count#__call__函数将类实例变成可调用形式,而实际上还会有一个返回量(变量/函数),因此需要写return,否则返回为None

test =Test()#以函数调用的形式直接调用类实例

print(test()) #1

print(test()) #2

这样看起来,类形式的装饰器有点像函数形式的装饰器,它也保存了一些变量。实际上这点不足为奇,因为,本来类实例的变量已经绑定在类实例对象中。

还可以这样用:

classAverage:def __init__(self):

self.values=[]#每次调用average实例都会更新self.values

def __call__(self, newvalue):

self.values.append(newvalue)

total=sum(self.values)

average= total /len(self.values)returnaverage

average=Average()print(average(6))print(average(7))print(average(8))

结果:6.0

6.5

7.0

类装饰器:

classDecorator:def __init__(self, add=1): #定义可以传入的参数

self.count =0

self.add=adddef __call__(self, fun):

self.fun=funreturnself._call_funcdef_call_func(self):

self.count+=self.addreturnself.fun(self.count)#相当于Decorate(count)

@Decorator(add=2) #改变传入的参数值

defcount(cnt):print(cnt)

count()#2

count() #4

笔者认为类形式的装饰器会比函数形式的装饰器更加灵活和方便,因为它的内部实现可以更灵活,看起来也比较符合日常使用的习惯,因为函数式的装饰器看起来总有一点怪怪的(笔者本人看法而已)。实际使用中就要根据业务需求来选择了

参考文章:

1. 《流畅的python》

2. https://docs.python.org/2/library/inspect.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值