返回介绍

为什么使用装饰器(重访)

发布于 2024-01-29 22:24:14 字数 1638 浏览 0 评论 0 收藏 0

那么,为什么我们只是展示不使用装饰器的方法来实现单体呢?正如我在本章开始的时候提到的,装饰器展示给我们利弊权衡。尽管语法意义重大,当面对新工具的时候,我们通常都忘了问“为什么要用”的问题。既然已经看到了装饰器实际是如何工作的,让我们花点时间在这里看看更大的问题。

就像大多数语言功能一样,装饰器也有优点和缺点。例如,从负面的角度讲,类装饰器有两个潜在的缺陷:

类型修改

正如我们所见到的,当插入包装器的时候,一个装饰器函数或类不会保持其最初的类型——其名称重新绑定到一个包装器对象,在使用对象名称或测试对象类型的程序中,这可能会很重要。在单体的例子中,装饰器和管理函数的方法都为实例保持了最初的类类型;在跟踪器的代码中,没有一种方法这么做,因为需要有包装器。

额外调用

通过装饰添加一个包装层,在每次调用装饰对象的时候,会引发一次额外调用所需的额外性能成本——调用是相对耗费时间的操作,因此,装饰包装器可能会使程序变慢。在跟踪器代码中,两种方法都需要每个属性通过一个包装器层来指向;单体的示例通过保持最初的类类型而避免了额外调用。

类似的问题也适用于函数装饰器:装饰和管理器函数都会导致额外调用,并且当装饰的时候通常会发生类型变化(不装饰的时候就没有)。

也就是说,这二者都不是非常严重的问题。对于大多数程序来说,类型差异问题不可能有关系,并且额外调用对速度的影响也不显著;此外,只有当使用包装器的时候才会产生后一个问题,且这个问题常常可以忽略,因为需要优化性能的时候可以直接删除装饰器,并且添加包装逻辑的非装饰器解决方案也会导致额外调用的问题(包括我们将在第39章学习的元类)。

相反,正如我们在本章开始所见到的,装饰器有3个主要优点。与前面小节的管理器(即辅助)函数解决方案相比,装饰器提供:

明确的语法

装饰器使得扩展明确而显然。它们的@比可能在源文件中任何地方出现的特殊代码要容易识别,例如,在单体和跟踪器实例中,装饰器行似乎比额外代码更容易被注意到。此外,装饰器允许函数和实例创建调用使用所有Python程序员所熟悉的常规语法。

代码可维护性

装饰器避免了在每个函数或类调用中重复扩展的代码。由于它们只出现一次,在类或者函数自身的定义中,它们排除了冗余性并简化了未来的代码维护。对于我们的单体和跟踪器示例,要使用管理器函数的方法,我们需要在每次调用的时候使用特殊的代码——最初以及未来必须做出的任何修改都需要额外的工作。

一致性

装饰器使得程序员忘记使用必需的包装逻辑的可能性大大减少。这主要得益于两个优点——由于装饰是显式的并且只出现一次,出现在装饰的对象自身中,与必须包含在每次调用中的特殊代码相比较,装饰器促进了更加一致和统一的API使用。例如,在单体示例中,可能更容易忘了通过特殊代码来执行所有类创建调用,而这将会破坏单体的一致性管理。

装饰器还促进了代码的封装以减少冗余性,并使得未来的维护代价最小化。尽管其他的编码结构化工具也能做到这些,但装饰器使得这对于扩展任务来说更自然。

然而,这三个优点还不是使用装饰器语法的必需的原因,装饰器的用法最终还是一个格式选择。也就是说,大多数程序员发现了一个纯粹的好处,特别是它作为正确使用库和API的一个工具。

我还记得类中的构造函数函数的支持者和反对者也有过类似的争论——在介绍__init__方法之前,创建它的时候通过一个方法手动地运行一个实例,往往也能实现同样的效果(例如,X=Class().init())。然而,随着时间的流逝,尽管这基本上是一个格式的选择,但__init__语法也变成了广泛的首选,因为它更为明确、一致和可维护。尽管这应该由你来决定,但装饰器似乎把很多同样的成功摆到了桌面上。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文