Python具有强大的功能和富有表现力的语法。我最喜欢的装饰品之一。在设计模式的上下文中,装饰器动态更改方法或类功能,而不必直接使用子类。当您需要扩展功能但不想修改原始功能时,这是理想的选择。我们可以在任何地方实现装饰器模式,但Python通过提供更具表现力的特性和语法来促进实现。在本文中,我将讨论Python的函数装饰器,并通过一些示例来阐明这些概念。所有示例均适用于Python2.7,但相同的概念应该适用于Python3,但语法有所更改。本质上,装饰器起到了包装器的作用,在不修改函数本身的情况下,修改目标函数执行前后代码的行为,从而增强原有的功能,从而对其进行装饰。您需要了解的功能在深入研究之前,您应该清楚一些先决条件。在Python中,函数是一等公民,它们是对象,这意味着我们可以用它们做很多有用的事情。将函数分配给变量defgreet(name):return"hello"+namegreet_someone=greetprint(greet_someone("John"))#output:helloJohndefinefunctioninsideotherfunctiondefgreet(name):defget_message():return"Hello"result=get_message()+namereturnresultprint(greet("John"))#输出:HelloJohn可以将函数作为参数传递给其他函数defgreet(name):return"Hello"+namedefcall_func(func):other_name="John"returnfunc(other_name)print(call_func(greet))#Output:HelloJohnfunctioncanreturnotherfunctions换句话说,函数生成其他函数。defcompose_greet_func():defget_message():返回“你好!”returnget_messagegreet=compose_greet_func()print(greet())#输出:你好!内部函数可以访问通常称为闭包的封闭范围。构建装饰器时会遇到的一种非常强大的模式。另一件需要注意的事情是Python只允许对外部范围进行读取访问,而不允许赋值。请注意我们如何修改上面的示例以从内部函数的封闭范围读取“name”参数并返回新函数。defcompose_greet_func(name):defget_message():return"Hellothere"+name+"!"returnget_messagegreet=compose_greet_func("John")print(greet())#输出:你好约翰!Composefunctiondecoratorfordecorator只是对现有函数的包装。把它们放在一起,我们可以构建一个装饰器。在这个例子中,我们考虑一个函数,它通过p标签包装另一个函数的字符串输出。defget_text(name):return"loremipsum,{0}dolorsitamet".format(name)defp_decorate(func):deffunc_wrapper(name):return"
{0}
".格式(func(name))returnfunc_wrappermy_get_text=p_decorate(get_text)print(my_get_text("John"))#Output:loremipsum,Johndolorsitamet
这是我们的第一个装饰。将另一个函数作为参数的函数,生成一个扩展原始函数功能的新函数,并返回生成的函数,以便我们可以在任何地方使用它。要让get_text本身被p_decorate装饰,我们只需将p_decorate的结果重新分配给get_text。get_text=p_decorate(get_text)print(get_text("John"))#Output:loremipsum,Johndolorsitamet
另一件需要注意的事情是我们的装饰器函数接受一个名称参数。我们在装饰器中要做的就是让get_text的包装器传递该参数。Python的装饰器语法Python使用一些语法糖来使创建和使用装饰器对程序员来说更简洁、更友好。get_text不需要修饰,get_text=p_decorator(get_text)它有个捷径,就是在要使用的函数前提供修饰函数的名字。装饰器的名字应该有一个@符号。defp_decorate(func):deffunc_wrapper(name):返回"{0}
".format(func(name))returnfunc_wrapper@p_decoratedefget_text(name):return"loremipsum,{0}dolorsitamet".format(name)print(get_text("John"))#Output:loremipsum,Johndolorsitamet
现在,让我们考虑一下我们要用其他2个函数function装饰get_text在字符串输出周围包装div和强标签。defp_decorate(func):deffunc_wrapper(name):return"{0}
".format(func(name))返回func_wrapperdefstrong_decorate(func):deffunc_wrapper(name):return"{0}".format(func(name))returnfunc_wrapperdefdiv_decorate(func):deffunc_wrapper(name):return"loremipsum,Johndolorsitamet
{0}
".format(func(self))返回func_wrapperclassPerson(object):def__init__(self):self.name="John"self.family="Doe"@p_decoratedefget_fullname(self):returnself.name+""+self.familymy_person=Person()print(my_person.get_fullname())更好的方法是让装饰器起作用并且方法很有用。这可以通过将*args和**kwargs作为参数传递给包装器来完成,然后包装器可以接受任意数量的参数和关键字参数。defp_decorate(func):deffunc_wrapper(*args,**kwargs):返回“{0}
”.format(func(*args,**kwargs))returnfunc_wrapperclassPerson(object):def__init__(self):self.name="John"self.family="Doe"@p_decoratedefget_fullname(self):returnself.name+""+self.familymy_person=Person()print(my_person.get_fullname())将参数传递给装饰器回顾上面前面的示例,您会注意到示例中的装饰器有多么冗余。3个装饰器(div_decorate、p_decorate、strong_decorate)具有相同的功能,但用不同的标签包装字符串。我们绝对可以做得更好。为什么不为将标签包装为字符串的标签提供更通用的实现?是的,请!deftags(tag_name):deftags_decorator(func):deffunc_wrapper(name):返回“<{0}>{1}{0}>”.format(tag_name,func(name))returnfunc_wrapperreturntags_decorator@tags("p")defget_text(name):return"Hello"+nameprint(get_text("John"))#OutputHelloJohn
在这种情况下,需要做更多的工作。装饰器期望接收一个函数作为参数,这就是为什么我们必须构建一个装饰器来接受这些额外的参数并动态生成它。在上面的示例标签中,是我们的装饰器生成器。调试装饰函数最后,装饰器只是包装我们的函数,以防调试出现问题,因为包装函数不携带原始函数的名称、模块和文档字符串。基于上面的例子,如果我们这样做:print(get_text.__name__)#outputfunc_wrapper期望输出get_text,然而,get_text的__name__、__doc__和__module__属性被包装器(func_wrapper)覆盖了。显然,我们可以在func_wrapper中重置它们,但Python提供了更好的方法。救援工具幸运的是,Python(从2.5版开始)包括functools模块,该模块包含functools.wraps。Wraps是一个装饰器,它将包装函数(func_wrapper)的属性更新为原始函数(get_text)的属性。这就像通过@wraps(func)装饰func_wrapper一样简单。这是一个更新的示例:fromfunctoolsimportwrapsdeftags(tag_name):deftags_decorator(func):@wraps(func)deffunc_wrapper(name):return"<{0}>{1}{0}>"。format(tag_name,func(name))returnfunc_wrapperreturntags_decorator@tags("p")defget_text(name):"""返回一些文本"""return"Hello"+nameprint(get_text.__name__)#get_textprint(get_text.__doc__)#returnssometextprint(get_text.__module__)#__main__你可以从输出中注意到get_text的属性现在是正确的。在何处使用装饰器相对于您可以使用装饰器完成的工作量,本文中的示例非常简单。它们可以为您的程序提供如此强大的功能。通常,装饰器非常适合扩展我们不想修改的函数的行为。有关有用的装饰器的广泛列表,我建议您查看Python装饰器库