当前位置: 首页 > 科技观察

Python的装饰器(Decorator)无非就是这样,我想多了

时间:2023-03-12 00:09:42 科技观察

1。Python装饰器的叠加使用最近有同学问Python也有类似Java的@xxxx语法。这是什么意思?现在我来回答这个问题。Java中的@xxxx语法是注解(Annotation),而Python中的@xxxx语法是装饰器(decorator)。尽管它们在语法上相似,但它们的功能却完全不同。Java的注解相当于句法元素(方法、类、接口等)的元数据。Python的装饰器是对Python函数(方法)的包装器,现在举个例子。@makebold@makeitalicdefsay():return"Hello"print(say()))这段代码对say函数使用了两个装饰器:@makebold和@makeitalic,并且是叠加状态。@makeitalic将首先作用于say函数,然后@makebold将作用于@makeitalic装饰器的结果。这两个装饰器分别用...和...包裹了say函数返回的字符串,所以这段代码的执行结果如下:你好,但是直接执行这段代码肯定会报错,因为这两个装饰器还没有定义,让我们看看如何定义这两个装饰器。2.定义Python装饰器装饰器本身就是一个普通的Python函数,但是函数的参数需要是一个函数类型(通常传递给被装饰的函数),定义形式如下:Hello现在定义上面给出的两个装饰器:fromfunctoolsimportwrapsdefmakebold(fn):@wraps(fn)defmakebold_wrapped(*args,**kwargs):return""+fn(*args,**kwargs)+""returnmakebold_wrappeddefmakeitalic(fn):@wraps(fn)defmakeitalic_wrapped(*args,**kwargs):return""+fn(*args,**kwargs)+"”returnmakeitalic_wrapped显然,makebold和makeitalic是两个普通的Python函数,函数内部还定义了另外两个函数,将这两个函数作为返回值返回。这个用到了wraps函数,这个函数其实可以省略,但是会有一些副作用。当使用@makebold和@makeitalic修饰一个函数时,被修饰的函数会被传递给makebold函数和makeitalic函数,也就是说fn参数是被修饰的函数。当外部调用修饰函数时,实际调用的是装饰器返回的函数,即makebold_wrapped和makeitalic_wrapped,这会导致修饰函数的属性发生变化,比如函数名、函数文档等,这可以先删除。@wraps,执行如下代码:@makeitalic@makebolddefsay():return"Hello"print(say.__name__)#输出函数名会输出如下内容:makebold_wrapped由于最后使用了@makebold装饰器,所以输出是makebold函数返回的makebold_wrapped函数的名称。如果添加@wraps,say将被输出。需要注意的是,wraps函数需要通过装饰器来调用,实际上相当于在@makebold外面包裹了一层装饰器(wraps)。3.理解Python函数现在我们已经学会了如何自定义Python装饰器,但是我们应该如何理解装饰器呢?原理是什么?要理解Python装饰器,首先应该知道Python函数是对象。看下面的例子:defshout(word="yes"):returnword.capitalize()#Output:Yesprint(shout())#将shout函数赋给另一个变量,这里不用括号,#所以不用调用function,将函数赋值给另一个变量,即给函数起一个别名scream=shout#可以用scream来调用shout函数#Output:Yesprint(scream())#目前同一个函数有两个引用:screamandshout,youcanusedeldeleteareferencedelshouttry:#引用删除后,函数无法通过引用调用print(shout())exceptNameError:print(e)#函数仍然可以通过引用调用另一个参考#Output:Yesprint(scream())这段代码演示了函数作为对象的使用。如果添加一对括号,则调用该函数。如果不加一对括号,函数就是一个对象,可以赋值给另一个变量,也可以作为函数参数值传入函数。由于Python函数本身是一个对象,所以可以在任何地方定义,包括函数内容。这是Python内置函数。代码如下:deftalk():#Inlinefunctiondefwhisper(word="YES"):returnword.lower()+"..."#调用内嵌函数print(whisper())#Calltalk,thewhispertalk函数内部调用#Output:yes...talk()try:#但是whisper函数不在talk函数外部可以看出调用会抛出异常print(whisper())exceptNameError:print(e)现在总结一下,Python函数的特点如下:(1)函数本身可以赋值给一个变量,或者作为参数值传入Function(方法);(2)可以在函数(方法)内部定义;有了这两个特性,就意味着该函数可以被另一个函数返回,看下面的代码:defgetTalk(kind="shout"):#定义第一个内置函数defshout(word="yes"):returnword。capitalize()+"!"#定义第二个内建函数defwhisper(word="yes"):returnword.lower()+"..."#根据参数值返回具体函数ifkind=="shout":#这里没有用一对括号,所以不是调用函数,而是返回函数本身:.shoutat0x7f93a00475e0>print(talk)#调用talk函数(其实就是shout函数)print(talk())#outputs:Yes!#CallwhisperFunctionprint(getTalk("whisper")())中这段代码,getTalk函数根据kind参数的值返回不同的内置函数,所以getTalk函数的返回值是函数本身,或者一个function对象,如果你想调用函数,你需要使用一对括号,比如getTalk()()根据这个特性,我们可以做更多的事情,比如在调用一个函数之前自动完成其他工作,看下面的代码:defdoSomethingBefore(func):print("")print(func())doSomethingBefore(talk)其实这段代码用doSomethingBefore函数包装了talk,这样就可以通过doSomethingBefore函数调用talk函数,在调用talk函数之前输出一行文字。4.Python装饰器的原理了解了Python函数之后,再理解Python装饰器就容易多了。废话不多说,先看下面的代码:#装饰器函数,参数是另一个函数(装饰后的函数)defmy_shiny_new_decorator(a_function_to_decorate):#装饰器的内嵌函数,用来包装装饰后的函数defthe_wrapper_around_the_original_function():#Output调用装饰函数前的一行文本print("Beforethefunctionruns")#调用装饰函数a_function_to_decorate()#调用装饰函数后输出一行文本print("Afterthefunctionruns")#返回包装函数returnthe_wrapper_around_the_original_function#该函数将bemy_shiny_new_decorator函数修饰defa_stand_alone_function():print("Iamastandalonefunction,don'tyoudaremodifyme")#调用函数a_stand_alone_function()#修饰a_stand_alone_function函数a_stand_alone_function_decorated=my_shiny_new_decorator(a_stand_alone_function)a_stand_alone_function_decorated()执行这段代码,会输出如下内容:Iamastandalonefunction,don'你敢在函数运行之前修改我andalonefunction,don'tyoudaremodifymeAfterthefunctionruns在这段代码中,a_stand_alone_function函数被my_shiny_new_decorator函数修改,调用a_stand_alone_function函数前后输出一行文字。事实上,这就是Python装饰器的用途:包装函数。只是这里没有使用装饰器的语法,而是直接调用装饰器函数,以最简单的方式修改a_stand_alone_function函数。如果a_stand_alone_function函数用装饰器修饰,可以使用如下代码。@my_shiny_new_decoratordefa_stand_alone_function():print("Iamastandalonefunction,don'ttyoudaremodifyme")这时候调用a_stand_alone_function函数,会自动使用my_shiny_new_decorator函数对a_stand_alone_function函数进行包装,即@my_shiny_new_decorator是my_shiny_new_andc_decorator的缩写形式()本文转载自微信公众号“极客起源”,可通过以下二维码关注。转载本文请联系极客本源公众号。