我在网上浏览了一些关于装饰器的教程。 我很难看到它们对于简单示例的好处。 这是一个迫切需要装饰的函数的常见示例,摘自此页面:
# Unelegant decoration
#---------------------
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
decorated_func = make_pretty(ordinary)
decorated_func()
# Output
#-------
# I got decorated
# I am ordinary
装饰器的好处描述如下:“Python 提供了一种更优雅的方式来实现此功能,而不是将函数调用分配给变量”。
# Elegant decoration
#-------------------
@make_pretty
def ordinary():
print("I am ordinary")
ordinary()
# Output
#-------
# I got decorated
# I am ordinary
其他教程提供了类似的示例和类似的动机,例如,here。
我对这个解释的困难在于,它不太符合在不修改它的情况下向要装饰的函数添加功能的意图(装饰器的另一个常见解释)。 在上面的示例中,未修饰的“ordinary()”不再可用,因此对其进行修饰实际上并不会在不需要或不需要修饰的情况下使原始函数可供使用。
另一个更具体的动机是通过“不将函数调用分配给变量”来实现更大的优雅。 然而,对于“不优雅的装饰”代码,如果没有上面的“优雅的装饰”代码,这很容易实现:
make_pretty(ordinary)()
# Output
#-------
# I got decorated
# I am ordinary
教程通常会在函数带参数的情况下描述装饰器。 我无法理解他们的动机,因为我什至无法理解上述最简单情况下的好处。 SO 问答还讨论了实际用例(例如,here),但是当上面最简单的情况的动机尚不清楚时,很难理解装饰器的原因。
是否可以用简单的语言说明上面最简单的情况(没有函数参数)的好处是什么? 或者只有通过某种方式弄清楚更复杂的情况才能清楚地看到好处?
装饰器的要点与大多数其他功能类似:模块化和“不要重复自己”。
如果您有一堆函数,它们应该在完成实际工作之前和之后打印某些消息,那么您可以在每个函数中添加
print()
调用。但这是乏味且重复的。因此,您可以定义一个在定义每个函数时使用的装饰器,它会自动将这些 print()
调用添加到所有函数中。
这也提供了模块化。如果您想更改打印的消息,只需在一个地方(装饰器)即可完成,而不必编辑所有函数。
许多装饰器比您展示的玩具示例复杂得多。考虑在类中定义属性时使用的内置
@property
装饰器,它将命名方法转换为可以像属性一样使用的属性。或者 @functools.cache
为您定义的函数添加自动缓存。像这样的库中的装饰器可以轻松地向您正在编写的函数添加功能。