在Python中添加了装饰器,以使函数和方法包装(接收函数并返回增强函数的函数)更易于阅读和理解。最初的用例是能够在定义的顶部将方法定义为类方法或静态方法。没有装饰器语法,将需要一个相当稀疏且重复的定义:
classWithoutDecorators:defsome_static_method():print("this is static method")some_static_method=staticmethod(some_static_method)defsome_class_method(cls):print("this is class method")some_class_method=classmethod(some_class_method)
如果将装饰器语法用于相同目的,则代码将更短且更易于理解:
classWithDecorators:@staticmethoddefsome_static_method():print("this is static method")@classmethoddefsome_class_method(cls):print("this is class method")
通用语法和可能的实现
装饰器通常是一个命名对象(不允许使用lambda表达式),该对象在被调用时将接受单个参数(它将成为装饰后的函数)并返回另一个可调用对象。此处使用“可调用”代替带有预想的“功能”。尽管装饰器通常在方法和功能的范围内进行讨论,但它们不限于此。实际上,任何可调用的对象(实现_call__方法的任何对象都被视为可调用对象)可以用作修饰符,并且它们返回的对象通常不是简单的函数,而是更多复杂类的实例,这些实例实现了自己的__call_方法。
装饰器语法只是一个语法糖。考虑以下装饰器用法:
@some_decoratordefdecorated_function():pass
总是可以用显式的装饰器调用和函数重新分配来代替:
defdecorated_function():passdecorated_function=some_decorator(decorated_function)
但是,如果在单个函数上使用多个装饰器,则后者的可读性较低,并且也很难理解。可以以多种不同方式使用装饰器,如下所示:
作为功能
编写自定义装饰器的方法有很多,但是最简单的方法是编写一个函数,该函数返回包装原始函数调用的子函数。
通用模式如下:
defmydecorator(function):defwrapped(*args,**kwargs):# do some stuff before the original# function gets calledresult=function(*args,**kwargs)# do some stuff after function call and# return the resultreturnresult# return wrapper as a decorated functionreturnwrapped
上课
尽管装饰器几乎总是可以使用函数来实现,但在某些情况下,使用用户定义的类是更好的选择。当装饰器需要复杂的参数化或取决于特定状态时,通常会发生这种情况。
非参数化装饰器作为类的通用模式如下:
classDecoratorAsClass:def__init__(self,function):self.function=functiondef__call__(self,*args,**kwargs):# do some stuff before the original# function gets calledresult=self.function(*args,**kwargs)# do some stuff after function call and# return the resultreturnresult
参数化装饰器
在实际代码中,经常需要使用可以参数化的装饰器。当将该函数用作装饰器时,解决方案很简单-必须使用第二层包装。这是装饰器的一个简单示例,该装饰器每次被调用都会重复执行装饰函数指定次数:
defrepeat(number=3):"""Cause decorated function to be repeated a number of times.
Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""defactual_decorator(function):defwrapper(*args,**kwargs):result=Nonefor_inrange(number):result=function(*args,**kwargs)returnresultreturnwrapperreturnactual_decorator
通过这种方式定义的装饰器可以接受参数:
>>>@repeat(2)...deffoo():...print("foo")...>>>foo()foo
foo
请注意,即使参数化装饰器的参数具有默认值,也必须在其名称后加上括号。使用带有默认参数的前面装饰器的正确方法如下:
>>>@repeat()...defbar():...print("bar")...>>>bar()bar
bar
bar
最后,让我们看看带有Properties的装饰器。
物产
这些属性提供了一个内置的描述符类型,该描述符类型知道如何将属性链接到一组方法。一个属性带有四个可选参数:fget,fset,fdel和doc。可以提供最后一个来定义链接到属性的文档字符串,就好像它是方法一样。这是一个Rectangle类的示例,可以通过直接访问存储两个角点的属性或使用width和height属性来控制它:
classRectangle:def__init__(self,x1,y1,x2,y2):self.x1,self.y1=x1,y1
self.x2,self.y2=x2,y2def_width_get(self):returnself.x2-self.x1def_width_set(self,value):self.x2=self.x1+valuedef_height_get(self):returnself.y2-self.y1def_height_set(self,value):self.y2=self.y1+value
width=property(_width_get,_width_set,doc="rectangle width measured from left")height=property(_height_get,_height_set,doc="rectangle height measured from top")def__repr__(self):return"{}({}, {}, {}, {})".format(self.__class__.__name__,self.x1,self.y1,self.x2,self.y2)
创建属性的最佳语法是使用属性作为装饰器。这将减少类内部方法签名的数量,并使代码更具可读性和可维护性。使用装饰器,以上类变为:
classRectangle:def__init__(self,x1,y1,x2,y2):self.x1,self.y1=x1,y1
self.x2,self.y2=x2,y2@propertydefwidth(self):"""rectangle height measured from top"""returnself.x2-self.x1@width.setterdefwidth(self,value):self.x2=self.x1+value@propertydefheight(self):"""rectangle height measured from top"""returnself.y2-self.y1@height.setterdefheight(self,value):self.y2=self.y1+value