前一段时间学习了python当中的装饰器,主要利用了闭包的原理。后来呢,又见到了python当中的functools模块,里面有很多实用的功能。今天我想分享一下跟装饰器息息相关的两个函数partial和wraps,这两个好伙伴可以说是非常实用。
1 partial偏函数: 这个函数可以帮助我们对一个接收多个参数的函数绑定其中几个参数,返回一个对象,调用这个对象的时候只传给他剩下的参数就可以。它实现了好像闭包才能实现的功能。有的时候可以代替闭包来使用。
不理解??没关系,听我慢慢说!
比如,我们现在想要编写一个函数帮助我们计算直线在坐标系上的y值。 直线方程y = a*x+b。大家请看代码:
1 def line( a , b , x ):2 return a*x + b3 4 5 if __name__ == '__main__':6 y1 = line( 10 , 20 , 30)7 y2 = line( 10 , 20 , 35)8 y3 = line( 10 , 20 , 7)
这个函数能够帮助我们完成功能,给定直线方程的系数a和b,再给定x就能计算出y。
但是我们发现,如果我们想多次利用同一条直线,比如 y = 10*x+20,我们每次传不同的x进去的时候a和b的值也必须传进去,我们重复做了很多工作。
我们怎么样能获得一条a和b指定的直线呢?我们只传进去x就能获取y,相同的直线不用每次都指定a和b??当然可以,用闭包就能实现!
如果还不会闭包的知识的伙伴,你们可以参考这篇博文学习一下闭包:http://www.cnblogs.com/Lin-Yi/p/7305364.html
我们先用闭包实现,上代码!
1 def getLine( a , b ): 2 def line( x ): 3 return a*x + b 4 return line 5 6 7 if __name__ == '__main__': 8 line1 = getLine( 10,20 ) 9 y1 = line1(5)10 y2 = line1(6)11 y3 = line1(7)
在这个闭包例子中,我们调用getLine传入a和b,之后我们拿到了内部的line函数,我们再调用line的时候只要给他传入x就可以,这样我们能重复利用同一条直线。
但是其实编写闭包挺麻烦的,我们要写内涵数外函数,而且很多好伙伴可能不理解闭包是怎么回事。这个时候福利来了!!!!!
我们用partial偏函数来实现一个绑定参数的直线!!看代码!
import functoolsdef line( a, b, x ): return a*x + bif __name__ == '__main__': #拿到了 y= 10*x + 20 line1 = functools.partial( line, 10 , 20 ) y1 = line1(30) y2 = line1(35) y2 = line1(35) y3 = line1(40)
本来呢,line函数接收三个参数a b和x,但是用partial,把line函数的引用穿进去,之后跟着两个参数,这就代表我要把line函数的前两个参数绑定我传入的指定数值10和20,然后返回一个对象给了line1
我们再调用line1的时候只要传入x就可以执行line函数了!!!
怎么样!是不是很神奇,我们用partial偏函数绑定了一个函数的部分参数,然后拿到一个函数对象,帮我们实现了闭包才能实现的效果!
2 wraps包裹: 帮助我们实现装饰器中,目标函数对象的属性迁移。
不理解??刚开始我也不理解~ . ~........ 那就听我说说~也许就懂啦!
上一次在探讨装饰器的时候,最后一个例子中留了一个问题没有解决,问题就是,我们用装饰器对目标函数dest进行装饰,用@decorator的时候,把目标函数dest传入了装饰器decorator,装饰器把内部函数inner的引用返回给了目标函数名dest。当我们再调用dest的时候实际上调用了装饰器返回的内部函数inner的实例对象。如果我们查看一下dest.__name__和dest.__doc__等等这些属性,发现我们获得的是inner的属性不是原本我们想要的dest的属性。
看我的描述很糊涂吧,我们来一段代码!看一下,装饰器带来了什么问题:
def decorator(func): def inner( *args ,**kwargs ): print("装饰器的前置业务逻辑") res = func( *args ,**kwargs ) #在这里执行目标函数 print("装饰器的后置业务逻辑") return res return inner@decorator #这里相当于发生了 dest = decorator( dest ) # 把目标函数dest传进去给了func,返回来的inner给了destdef dest(): #之后再执行dest 其实执行了inner print("这里是目标函数dest")if __name__ == '__main__': print(dest.__name__) #inner
我们用decorator装饰器对dest目标函数装饰之后,再查看dest.__name__ 却得到了inner,,因为内涵数返回给了dest保存,再调用dest其实就是调用了inner
这是正确的却不是我们想要的。我们抛开装饰器,在外部看来,我们还是想要得到一致性的结果,我指向要我自己的属性,怎么办??
ok!wraps能够帮我们实现属性的迁移!
上一段代码!
import functoolsdef decorator(func): #functool模块的wraps函数传入目标函数,他能帮我们把目标函数的所有属性迁移给内涵数 @functools.wraps(func) def inner( *args ,**kwargs ): print("装饰器的前置业务逻辑") res = func( *args ,**kwargs ) #在这里执行目标函数 print("装饰器的后置业务逻辑") return res return inner@decorator #这里相当于发生了 dest = decorator( dest ) # 把目标函数dest传进去给了func,返回来的inner给了destdef dest(): #之后再执行dest 其实执行了inner print("这里是目标函数dest")if __name__ == '__main__': #这是再查看属性的时候 就保持了一致性,wraps帮助我们把目标函数的属性迁移了 print(dest.__name__) #dest
和上面的例子只有一行代码的区别,在inner函数前加上了@functools.wraps(func)
把目标函数传给wraps,用wraps对inner进行装饰,它帮助我们把目标函数的属性全都复制过来了。让外部调用者看来保持了一致性!