python函数的封装,python函数装饰器详解

  python函数的封装,python函数装饰器详解

  包含一般用来修饰函数,有些函数是附加在被修饰的函数上的。它的输入一般是一个函数,输出是同一个函数或者另一个函数。除了注册装饰器,大部分装饰器返回的函数对象都与被装饰的函数不同。另一方面,装饰部分的内部定义返回函数,所以涉及到嵌套函数的定义。“闭包”是嵌套函数正常工作的基础之一。闭包可以简单理解为“内部扩展了嵌套函数的作用域,可以引用闭包里的自变量”。

  为了便于解释,下面定义了一些基本名词概念。装饰器返回的函数对象是wrapper,装饰对象是wrapped。装饰者经常用语法糖的形式来装饰功能:

  # * * * * * *语法sugar format by func=decorate(func)* * * * * @ decorated effunc)x):pass使用这个限定方法时,包装器对象必须先了解update_wrapper函数和functools的分部类,才能知道如何使用包装器函数作为解决方案——使用wraps函数修饰函数装饰器。

  Update_wrapper函数update_wrapper是解决导入问题的核心,字面意思是update_wrapper函数更新包装器的信息,具体是将被修饰的函数对象的属性信息,赋值或者更新到wrapper中

  defupdate_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER _ UPDATES)3360 updateawwrapperfunctiontolooklikethewrappedfunctionwrapperisthefunctiontobeupdatedwwrappedistheoriginalfunctionasigng signeddirectlyfromthewrappedfunctiontowrapperfunction(defaultstofunctiontools . WRAPPER _ asign mment updatedisatuplenamenget datewiththecorresponding attributefromthewrappedfu function(tools . WRAPPER _ UPDATES) # WRAPPER _ assignment_ _ qual namement“_ _ annotations _ _ ”( for attrinassigned 3360 try:value=getattr(wrapped,attr ) exceptattributeerror 3360传递llor值(# wrapper _ updates=(_ _ dict _ ,)for attrin updated 3360 getatttr(wrapper){ })# issue # 174823360 set _ _ wrapped _ _ lastsowedon tinadventlycopyit # frohewrappedfunction当updating保存在包装器中时,__wrapped__ property,一般是修改后的函数# returnthewrapperthiscanusedascadecoratorviapartal()Return Wrapper _ u up der如果wrapped是func,那么包装器会保持好玩。

  c的信息,所以显示的是func的信息。例子如下:

  from operator import Add from func tools import update _ wrapper,partial,wrappers #首先定义一个decorator def my _ decorator(func): Add say utility to buildings Add print( begin decorator!)def包装(*args,**kwargs): 我是函数inner my _ decorator print( say hello!)result=func(*args,**kwargs) print(say bye!)返回结果打印(结束装饰!)返回包装器my_add=my _ decorator (add)下图显示了add和my_add的一些信息:

  首先可以看到“begin decorate”和“end decorator”的打印信息,这说明my_decorator运行正常。然后显示my_add的名称和文档字符串信息,与my_decorator的定义一致。稍后,将显示内置的add函数名和文档字符串信息。

  下图显示了装饰者赋予add内置函数的新函数say hello和say bye。

  现在使用update_wrapper函数将add函数的属性信息复制到my_add,assigned与updated关键字参数用默认设置就可以了:

  可以发现my_add1现在不仅有“说”的功能,而且my_add1的属性信息是内置函数add的属性信息。

  分部类首先注意分部是一个类,截取的部分源代码如下:

  class partial:“”部分应用给定参数和关键字的新函数 __slots__=func , args , __dict__ , __weakref__ def __new__(cls,func,/,*args,**keywords): if不可调用(func): raise TypeError(第一个参数必须可调用)if hasattr(func, func ):args=func . args args keywords={ * * func . keywords,* * func=func . func self=super(partial,cls)。_ _ new _ _(cls)self . func=func self . args=args self . keywords=keywords return self def __call__(self,/,*args,* * keywords):keywords={ * * self . keywords,* args,* keywords)return self . func(* self . args,* args,* * keywords)由于定义了_ _ call _ _方法,分部实例是一个可调用的对象,func实际对分部实例的调用可以这样理解。在调用func函数时,需要提供多个参数值,但是在实例化partial对象时,提供一些位置参数和关键字参数,这些参数值在patient实例对象中是固定的。在调用partial对象时,只需要提供几个参数,可以类比更高数的偏导数概念。下面给出了部分的应用实例:

  Add_fix=partial(add,99) # a参数print(add_fix(1)) #在固定加法运算中返回100,==99 1print(add_fix(2)) #返回101,==99 2

  wrapps函数wrapps函数是一个结合了部分类和update_wrapper的函数。包装的源代码定义如下:

  def wrappers(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES): 装饰器工厂将update_wrapper()应用于包装器函数,返回调用update_wrapper()的装饰器,其中装饰器函数作为包装器参数,WRAPPER()的参数作为其余参数。默认参数与update_wrapper()相同。这是一个方便的函数,可以简化将partial()应用于_ wrapper()的过程。#注意:update_wrapper是update_wrapper函数returnpartial (update _ wrapper,wrapped=wrapped,assigned=assigned,Updated=updated)部分实例化过程与update _ wrapper函数调用非常相似,只是缺少一个包装器参数。如果调用分部实例并提供包装器参数,则等效于update _ wrapper (wrapper,wrapped=wrapped,assigned=assigned,updated=updated)。最后三个关键参数值都是在分部实例化时存储的。

  请注意,包装需要放在包装之前,如下所示:

  @ wrapps(func)会依次执行wrapps(func)-partial(func,* args,* * kwargs),返回的partial实例是可调用的,假设p,p(wrapper)等价于update _ wrapper (wrapper,* * kwargs)。红框中的Wraps (func)返回以下内容

  结果是部分实例,实际上是固定了wrapped、assigned、updated三个关键字参数update_wrapper。当返回的分部实例用于修饰包装器时,相当于wrapper=partial(wrapper),被包装的属性信息被复制并更新到包装器属性信息中。所以可以看出,my_add对象带有wraps的属性,比如它的名字,就是内置函数add的属性,从而解决了前言中的问题。注意“wraps本质上是一个装饰器工程函数,因为它的返回值是一个分部实例,可以作为装饰器来装饰其他函数”。

  带参数的装饰器装饰器的输入参数通常是函数。如果需要定义一个带参数的装饰器,怎么实现呢?3354在装饰器定义的外层定义了一个“装饰器工厂函数”,所需装饰器的参数放在“装饰器工程函数”中,内层的装饰器可以访问外层的自由变量。如下例所示:

  DEF Decorator _ factory(count=true): 定义装饰器工厂函数 DEF Decorator(func): Real Decorator @ func tools . wrappers(func)DEF wrapper(* args): 装饰器内置函数 if count: # reference自由变量start=time . time()RES=func(* args)print(总成本时间:{} s 。format(time . time()-start))Return else:Return func(* args)Return wrapper Return decorator接下来,通过调用:

  @ decorator _ factory(count=True)def f1(n):如果n2: return n return f1 (n-1) f1 (n-2)当count=True时,表示计时功能开启,如下图:

  当count=False时,不启用定时功能,相当于f功能:

  总之,实现带参数的装饰器,最重要的有两点,1)定义装饰器工厂函数,将参数放入工厂函数定义中,工厂函数的返回值为实际需要的装饰器对象;2)使用语法糖形式时,一定要以函数调用的方式调用装饰器工厂函数,因为是调用,所以工厂函数会返回装饰器,然后再用返回的装饰器修饰目标函数

  参考python——第7章数据流的函数装饰器和闭包https://docs.python.org/3.7/librry/functools.html。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: