python中的装饰器

  对于初学者,第一次面对python中的装饰器肯定会觉得有点绕,但是装饰器的功能很强大,用法也很灵活。能够灵活运用装饰器对于实际开发中更能够如虎添翼。

  还是以实例来讲一下python中的装饰。

  def foo():

  print("----foo---")

  现在我们需要对foo函数增加日志功能,你可能想到的最简单方法就是在下面增加一行

  print(time.time())。但是现在需要在不改变函数的定义下增加这个功能呢,这种在不改变原有函数的定义下在代码运行期间动态得增加功能得方式就叫做装饰器。

 

  从运行结果可以看到,函数运行得结果不变,并且在上面打印了日志。这就达到了我们预期得效果。那么装饰器是怎么完成这个功能得呢?

  本质上,装饰器是一个返回函数的函数。借助python的@语法,把函数定义在函数的定义处,那么发生的作用就是:foo=log(foo)即原来定义的foo变量指向了log()函数返回的call_fun函数。

   那么foo()函数调用实际上就是call_func(),在call_func函数的代码块中,实现了打印日志 ,并且

依然能够调用目标函数。

   上面的函数是无参数的,那么对于传参的装饰器又该如何设计呢?上代码。

  在前面的分析中,我们可以知道,调用foo()函数实际上掉的是call_func()函数,因此只需要给call_func传入参数就行了。为了装饰器的通用性,可以将形参改为:*args,**kwargs。那么不管foo()函数是否传参数,或者传几个参数,都不需要再修改装饰器了。

  函数传参解决了,那么我们来解决一下装饰器传参数的问题。老规矩,上代码。

  从代码中可以看出,装饰器传参实际上就是将参数传入装饰器的闭包中,然后返回一个装饰器就可以了。即foo=log("log")(foo);

  写到现在,看起来没什么问题,但实际上函数的__name__属性发生了变化,即原函数foo的__name__在用装饰器装饰之前是'foo'

  这个理解其实不难,因为foo函数被装饰之后实际上指向了call_func的引用。

  那么当某些情况下需要用函数的名字来完成某些功能的时候就需要想办法避免这种错误了。

  在python中的functools模块其中的wraps函数就是干这个活儿的。

  在装饰器中的call_func定义上面使用@functools.wraps(func)进行装饰就能够保证原函数被装饰的同时__name__属性不会改变。

posted on 2017-08-14 21:21  君子生  阅读(96)  评论(0)    收藏  举报

导航