我对装饰器有基本的了解,但到目前为止它们似乎是多余的并且“hacky”,就像 C 宏但用于函数。
An article为了强调装饰器的重要性,给出了装饰器使用的示例:
from myapp.log import logger
def log_order_event(func):
def wrapper(*args, **kwargs):
logger.info("Ordering: %s", func.__name__)
order = func(*args, **kwargs)
logger.debug("Order result: %s", order.result)
return order
return wrapper
@log_order_event
def order_pizza(*toppings):
# let's get some pizza!
order_pizza(*toppings) # 用法
这不是相当于下面的无装饰器代码吗?
from myapp.log import logger
def log_order_event(func, *args, **kwargs):
logger.info("Ordering: %s", func.__name__)
order = func(*args, **kwargs)
logger.debug("Order result: %s", order.result)
return order
def order_pizza(*toppings):
# let's get some pizza!
log_order_event(order_pizza, *toppings) # 用法
事实上,第二个代码片段不是更容易编写吗,因为它是一个函数,而不是一个函数和一个包装函数?使用调用更长,但它们更清楚地表明实际调用的内容。
这纯粹是品味和语法糖的问题,还是我错过了什么?
最佳答案
就行为而言,两个片段是等效的。但是,我使用装饰器版本有两个主要原因:
- 可读性:对于代码片段 2,您需要对一个不太相关的方法进行显式函数调用
log_order_event()
,其主要目的是记录。其他方法(例如order_appetizers()
)可能需要此日志记录功能,因此,您需要为每个此类方法单独调用log_order_event()
。 (从可读性的角度来看)一次性定义一个执行日志记录的包装器,并且您可以在必要时为此类方法调用包装器(作为装饰器)不是更好吗?装饰器是语法糖,但肯定不是黑客。事实上,PEP 318清楚地讨论了装饰器背后的动机,你一定要读一下。 可扩展性:考虑这样一种情况,您的代码有多个方法(例如 20 个),例如
order_appetizers()
、order_maincourse()
、order_desserts()
等等。我们还假设您想在订购任何这些商品之前执行一些预先检查,例如检查配料(披萨)、卡路里(甜点)等。使用装饰器时,只需在每个检查的单独包装类中定义包装函数,将其导入到主代码中并分别装饰所需的方法即可。这样,您的包装器函数就完全隔离,并且主要逻辑保持干净且易于维护。@check_toppings @log_order_event def order_pizza(*toppings): # let's get some pizza! @check_calories @log_order_event def order_desserts(): # let's order some desserts! ...and so on for 20 more methods
使用无装饰器逻辑,虽然您可以将检查(和日志)方法分离到单独的类中并导入到主逻辑中,但您仍然需要向每个方法添加单独的调用在每个 order 方法中,从代码维护的角度来看,这可能会令人望而生畏,尤其是对于新手来说。
关于python - Python 装饰器有必要吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59493259/