欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

Python学习之wraps修饰器

发布时间:2017-05-15 23:28  回复:0  查看:2273   最后回复:2017-05-15 23:28  
本文和大家分享的主要是python wraps 修饰器相关内容,一起来看看吧,希望对大家 学习python有所帮助。
  在了解  wraps  修饰器之前,我们首先要了解  partial    update_wrapper  这两个函数,因为在  wraps  的代码中,用到了这两个函数。
   partial
  首先说  partial  函数,在  官方文档  的描述中,这个函数的声明如下:  functools.partial(func, *args, **keywords)  。它的作用就是返回一个  partial  对象,当这个  partial  对象被调用的时候,就像通过  func(*args, **kwargs)  的形式来调用  func  函数一样。如果有额外的  位置参数( args)  或者  关键字参数(*kwargs)  被传给了这个  partial  对象,那它们也都会被传递给 func  函数,如果一个参数被多次传入,那么后面的值会覆盖前面的值。
  个人感觉这个函数很像C++ 中的  bind  函数,都是把某个函数的某个参数固定,从而构造出一个新的函数来。比如下面这个例子:
   from functools  import partial
   def  add(x:int, y:int):
   return x+y
  这里创造了一个新的函数 add2 ,只接受一个整型参数,然后将这个参数统一加上 2
  add2 = partial(add, y=2)
  add2(3)  #  这里将会输出 5
  这个函数是使用C 而不是 Python 实现的,但是官方文档中给出了 Python 实现的代码,如下所示,大家可以进行参考:
   def  partial(func, *args, **keywords):
   def  newfunc(*fargs, **fkeywords):
  newkeywords = keywords.copy()
  newkeywords.update(fkeywords)
   return func(*args, *fargs, **newkeywords)
  newfunc.func = func
  newfunc.args = args
  newfunc.keywords = keywords
   return newfunc
   update_wrapper
  接下来,我们再来聊一聊  update_wrapper  这个函数,顾名思义,这个函数就是用来更新修饰器函数的,具体更新些什么呢,我们可以直接把它的源码搬过来看一下:
  WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
  '__annotations__')
  WRAPPER_UPDATES = ('__dict__',) def  update_wrapper(wrapper,
  wrapped,
  assigned = WRAPPER_ASSIGNMENTS,
  updated = WRAPPER_UPDATES):
   for attr  in assigned:
   try:
  value = getattr(wrapped, attr)
   except AttributeError:
   pass
   else:
  setattr(wrapper, attr, value)
   for attr  in updated:
  getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
  wrapper.__wrapped__ = wrapped
   return wrapper
  大家可以发现,这个函数的作用就是从  被修饰的函数(wrapped)  中取出一些属性值来,赋值给 修饰器函数(wrapper)  。为什么要这么做呢,我们看下面这个例子。
   自定义修饰器v1
  首先我们写个自定义的修饰器,没有任何的功能,仅有文档字符串,如下所示:
   def  wrapper(f):
   def  wrapper_function(*args, **kwargs):
  """ 这个是修饰函数 """
   return f(*args, **kwargs)
   return wrapper_function
  @wrapper def  wrapped():
  """ 这个是被修饰的函数 """
  print('wrapped')
  print(wrapped.__doc__)  #  输出 ` 这个是修饰函数 `
  print(wrapped.__name__)  #  输出 `wrapper_function`
  从上面的例子我们可以看到,我想要获取  wrapped  这个被修饰函数的文档字符串,但是却获取成了  wrapper_function  的文档字符串,  wrapped  函数的名字也变成了  wrapper_function  函数的名字。这是因为给  wrapped  添加上  @wrapper  修饰器相当于执行了一句  wrapped = wrapper(wrapped)  ,执行完这条语句之后,  wrapped  函数就变成了  wrapper_function  函数。遇到这种情况该怎么办呢,首先我们可以手动地在  wrapper  函数中更改  wrapper_function    __doc__  __name__  属性,但聪明的你肯定也想到了,我们可以直接用  update_wrapper  函数来实现这个功能。
   自定义修饰器v2
  我们对上面定义的修饰器稍作修改,添加了一句  update_wrapper(wrapper_function, f) 
   from functools  import update_wrapper
   def  wrapper(f):
   def  wrapper_function(*args, **kwargs):
  """ 这个是修饰函数 """
   return f(*args, **kwargs)
  update_wrapper(wrapper_function, f)  # <<   添加了这条语句
   return wrapper_function
  @wrapper def  wrapped():
  """ 这个是被修饰的函数 """
  print('wrapped')
  print(wrapped.__doc__)  #  输出 ` 这个是被修饰的函数 `
  print(wrapped.__name__)  #  输出 `wrapped`
  此时我们可以发现,  __doc__    __name__  属性已经能够按我们预想的那样显示了,除此之外,  update_wrapper  函数也对 __module__    __dict__  等属性进行了更改和更新。
   wraps修饰器
  OK ,至此,我们已经了解了  partial    update_wrapper  这两个函数的功能,接下来我们翻出  wraps  修饰器的源码:
  WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
  '__annotations__')
  WRAPPER_UPDATES = ('__dict__',) def  wraps(wrapped,
  assigned = WRAPPER_ASSIGNMENTS,
  updated = WRAPPER_UPDATES):
   return partial(update_wrapper, wrapped=wrapped,
  assigned=assigned, updated=updated)
  没错,就是这么的简单,只有这么一句,我们可以看出,  wraps  函数其实就是一个修饰器版的 update_wrapper  函数,它的功能和  update_wrapper  是一模一样的。我们可以修改我们上面的自定义修饰器的例子,做出一个更方便阅读的版本。
   自定义修饰器v3
   from functools  import wraps
   def  wrapper(f):    @wraps(f)
   def  wrapper_function(*args, **kwargs):
  """ 这个是修饰函数 """
   return f(*args, **kwargs)
   return wrapper_function
  @wrapper def  wrapped():
  """ 这个是被修饰的函数
  """
  print('wrapped')
  print(wrapped.__doc__)  #  输出 ` 这个是被修饰的函数 `
  print(wrapped.__name__)  #  输出 `wrapped`
  至此,我想大家应该明白  wraps  这个修饰器的作用了吧,就是将  被修饰的函数(wrapped)  的一些属性值赋值给  修饰器函数(wrapper)  ,最终让属性的显示更符合我们的直觉。
来源:SegmentFault
您还未登录,请先登录

热门帖子

最新帖子