不懂装饰器,就不是真正会 Python


编程部分2019.7.16我想分享

Decorator是Python中的一个特殊工具。它为我们提供了灵活的修改功能之外功能的能力。它有点像带有独特符号的魔术帽。只需将它佩戴在功能的顶部,您就可以默默地改变功能的行为。您可能已经处理过很多装饰设备。当我们进行面向对象的编程时,我们经常使用两个内置的装饰器。此外,如果您触摸了click模块,您将不会对装饰器不熟悉。 Click最受欢迎的参数定义界面是用装饰器实现的。除了使用装饰用具外,我们经常需要自己写一些装饰用具。在这篇文章中,我将从两个方面与您分享一些关于装饰的一些知识。最佳实践大多数装饰器都基于函数和闭包,但这不是制作装饰器的唯一方法。实际上,Python对于对象是否可以用作装饰器()只有一个要求:装饰器必须是“可调用”对象。函数自然是“可调用”对象。但除了函数之外,我们还可以使任何类都可调用。方法很简单,只要是自定义类的魔术方法。

基于此功能,我们可以轻松地使用类来实现装饰器。

下面的代码定义了一个名为Decorator的装饰器,它使用装饰函数在每次执行之前等待额外的秒数。同时,我们也希望为用户提供不需要等待立即执行的接口。

如何使用装饰器示例代码:

它是一个基于类的装饰器。当然,如果您非常熟悉Python中的函数和闭包,那么上面的装饰器也可以完全使用函数实现。那么为什么我们要用类来做呢?

与纯函数相比,我觉得使用类实现的装饰器在某些情况下有几个优点:

实现有状态装饰器时,操作类属性比操作闭包变量更直观,更不容易出错。

当实现一个充当函数扩展接口的装饰器时,使用类包装器函数比直接将属性附加到函数对象更容易维护

实现与装饰器和上下文管理器协议兼容的对象更容易(参见unitest.mock.patch)

你在编写装饰器的过程中遇到过任何不愉快的事吗?无论你是否拥有它,无论如何我都拥有它。在编写代码时,我经常会遇到以下两件事:

当使用参数实现装饰器时,分层功能代码特别难以编写且难以阅读

由于函数和类方法之间的区别,为前者编写的装饰器通常不会直接应用于后者。

例如,在下面的示例中,我实现了一个生成随机数的装饰器,并将其注入函数参数。

嵌套级别很深,不能在类方法上使用。如果直接使用它来装饰类方法,则会发生以下情况:

类实例中的方法将输出类实例而不是我们期望的随机数。这是因为方法和功能在工作机制上有细微差别。如果你想解决这个问题,装饰器必须巧妙地跳过隐藏在类方法的位置参数中的类实例变量,以便正确地作为第一个参数注入。此时,应该是压缩模块首次亮相的时候了。该模块是一个工具库,旨在帮助您编写装饰器。有了它,我们可以轻松修改装饰器来解决“嵌套层次深”和“无法通用”这两个问题,

用模块编写的装饰器与原版相比具有以下优点:

较少的嵌套级别:使用可以将两个级别的嵌套减少到一个层

更简单:在处理位置和关键字参数时,您可以忽略特殊情况,例如类实例

更灵活:判断后更容易使装饰器具有通用性。

常见错误

“设计模式”是一个在计算机世界中很有名的词。如果您是Java程序员并且您不了解设计模式,那么我敢打赌您肯定会通过面试过程。

但是当我们编写Python时,我们很少谈论“设计模式”。尽管Python也是一种面向对象的编程语言,但其鸭型设计和出色的动态性决定了大多数设计模式对我们来说并不是必需的。因此,许多Python程序员在工作很长时间后可能实际上没有应用多种设计模式。

但是,“装饰模式”是一个例外。因为Python的“装饰者”和“装饰者模式”具有完全相同的名称,所以我听说人们不止一次地将它们视为一件事,并认为使用“装饰者”正在练习“装饰模式”。但事实上,它们是两个完全不同的东西。

“装饰器模式”是完全源自“面向对象”的编程技术。它有几个关键组件:统一的接口定义,跟随接口的几个类,以及类和类之间的逐层包装。最终它们形成了“装饰”效果。

Python中的“装饰器”和“面向对象”之间没有直接的联系。它可能只是功能和功能之间的一个技巧。事实上,“装饰者”并没有提供不可替代的功能,它只是一种“语法糖”。以下代码使用装饰器:

基本上与以下内容相同:

装饰器的最大优点是我们可以在某些场景中编写更直观,易于阅读的代码。它只是一个“糖”,而不是面向对象域的复杂编程模型。

提示:有一个在Python官方网站上实现装饰器模式的例子。您可以阅读此示例以更好地理解它。

这是一个专门用于打印需要时间的函数调用的简单装饰器:

尽管装饰器没有错误,但在使用它来装饰函数之后,函数的原始签名将被破坏。换句话说,您无法再获取函数的名称,文档的内容以及所有签名将成为内部函数。

这是一个小问题,但它也可能在某些时候导致无法检测到的错误。幸运的是,标准库为它提供了解决方案。在定义装饰器时,您只需要用另一个装饰器来装饰内部函数。

听起来有点曲折,但它实际上增加了一行代码:

在此处理之后,装饰器不会影响它装饰的功能。

装饰器是功能对象的高级应用程序。在编写装饰器的过程中,您经常会遇到内部函数需要修改外部函数变量的情况。就像下面的装饰者一样:

为了计算函数调用的数量,我们需要修改函数内部外部函数定义的变量的值。但是,上面的代码是有问题的,解释器在执行它时会报告错误:

此错误是由与函数嵌套的作用域引起的。执行解释器时,不知道外部作用域中定义的变量。它被视为局部变量,并在当前范围中查找。最后,我没有找到关于变量的任何定义,然后抛出错误。为了解决这个问题,我们需要通过关键字告诉解释器:“count变量不属于当前的局部范围,去看外面。”之前的错误可以解决。

提示:有关非本地关键字历史的更多信息,请查看PEP-3104

在本文中,我与您分享了有关装饰器的一些提示和技巧。

总结了一些要点:

所有可调用对象都可用于实现装饰器

混合函数和类以更好地实现装饰器

wrapt模块非常有用,它可以帮助我们用更简单的代码编写复杂的装饰器

“装饰者”只是语法糖,它不是“装饰模式”

装饰者将改变函数的原始签名,你需要

当内部函数修改外部函数的变量时,需要使用关键字

看完文章后,你想呕吐吗?请在Project Github Issues上留言或告诉我。

回复以下“关键字”以获得高质量的资源

回复关键字“pybook03”,并立即与您的朋友一起获取主页翻译的《Think Python 2e》电子版

回复关键字“pybooks02”并从O'Reilly Press获得免费的Python相关电子书集

回复关键词“Book List 02”,并立即获得主页编译的10个Python引物的电子版。

豆瓣9.1分,中文版销售超过30万,零基础也可以用这本书学习Python

您想要的IT资源可能有

Python将超越C,Java并成为最流行的语言

如何编写优雅的Python函数?

标题地图:pexels,CC0授权。

收集报告投诉

Decorator是Python中的一个特殊工具,它使我们能够灵活地修改函数之外的函数。它有点像带有独特符号的魔法帽。只需将其佩戴在功能之上,您就可以默默地改变功能本身的行为。你可能已经处理过很多装饰器了。在进行面向对象编程时,我们经常使用两个内置装饰器。此外,如果您已经接触到click模块,那么您将不会对装饰器不熟悉。单击最着名的参数定义界面是使用装饰器实现的。除了使用装饰器之外,我们经常需要自己编写一些装饰器。在本文中,我将与您分享一些关于装饰器的一些知识和两个方面。最佳实践大多数装饰器都是基于函数和闭包实现的,但这不是制作装饰器的唯一方法。实际上,Python对于是否可以在decorator()形式中使用对象只有一个要求:装饰器必须是“可调用”对象。该函数自然是一个“可调用”对象。我们也可以使任何类“可调用”。方法很简单,只需定制类的魔术方法。

基于此功能,我们可以轻松地使用该类来实现装饰器。

下面的代码将定义一个名为的装饰器,并且用它装饰的函数将在每次执行之前等待一个额外的秒。同时,我们还希望为用户提供一个不必等待立即执行的界面。

如何使用装饰器的示例代码:

它是一个基于类的装饰器。当然,如果您非常熟悉Python中的函数和闭包,那么上面的装饰器也可以完全使用函数实现。那么为什么我们要用类来做呢?

与纯函数相比,我觉得使用类实现的装饰器在某些情况下有几个优点:

实现有状态装饰器时,操作类属性比操作闭包变量更直观,更不容易出错。

当实现一个充当函数扩展接口的装饰器时,使用类包装器函数比直接将属性附加到函数对象更容易维护

实现与装饰器和上下文管理器协议兼容的对象更容易(参见unitest.mock.patch)

你在编写装饰器的过程中遇到过任何不愉快的事吗?无论你是否拥有它,无论如何我都拥有它。在编写代码时,我经常会遇到以下两件事:

当使用参数实现装饰器时,分层功能代码特别难以编写且难以阅读

由于函数和类方法之间的区别,为前者编写的装饰器通常不会直接应用于后者。

例如,在下面的示例中,我实现了一个生成随机数的装饰器,并将其注入函数参数。

嵌套级别很深,不能在类方法上使用。如果直接使用它来装饰类方法,则会发生以下情况:

类实例中的方法将输出类实例而不是我们期望的随机数。这是因为方法和功能在工作机制上有细微差别。如果你想解决这个问题,装饰器必须巧妙地跳过隐藏在类方法的位置参数中的类实例变量,以便正确地作为第一个参数注入。此时,应该是压缩模块首次亮相的时候了。该模块是一个工具库,旨在帮助您编写装饰器。有了它,我们可以轻松修改装饰器来解决“嵌套层次深”和“无法通用”这两个问题,

用模块编写的装饰器与原版相比具有以下优点:

较少的嵌套级别:使用可以将两个级别的嵌套减少到一个层

更简单:在处理位置和关键字参数时,您可以忽略特殊情况,例如类实例

更灵活:判断后更容易使装饰器具有通用性。

常见错误

“设计模式”是一个在计算机世界中很有名的词。如果您是Java程序员并且您不了解设计模式,那么我敢打赌您肯定会通过面试过程。

但是当我们编写Python时,我们很少谈论“设计模式”。尽管Python也是一种面向对象的编程语言,但其鸭型设计和出色的动态性决定了大多数设计模式对我们来说并不是必需的。因此,许多Python程序员在工作很长时间后可能实际上没有应用多种设计模式。

但是,“装饰模式”是一个例外。因为Python的“装饰者”和“装饰者模式”具有完全相同的名称,所以我听说人们不止一次地将它们视为一件事,并认为使用“装饰者”正在练习“装饰模式”。但事实上,它们是两个完全不同的东西。

“装饰器模式”是完全源自“面向对象”的编程技术。它有几个关键组件:统一的接口定义,跟随接口的几个类,以及类和类之间的逐层包装。最终它们形成了“装饰”效果。

Python中的“装饰器”和“面向对象”之间没有直接的联系。它可能只是功能和功能之间的一个技巧。事实上,“装饰者”并没有提供不可替代的功能,它只是一种“语法糖”。以下代码使用装饰器:

基本上与以下内容相同:

装饰器的最大优点是我们可以在某些场景中编写更直观,易于阅读的代码。它只是一个“糖”,而不是面向对象域的复杂编程模型。

提示:有一个在Python官方网站上实现装饰器模式的例子。您可以阅读此示例以更好地理解它。

这是一个专门用于打印需要时间的函数调用的简单装饰器:

尽管装饰器没有错误,但在使用它来装饰函数之后,函数的原始签名将被破坏。换句话说,您无法再获取函数的名称,文档的内容以及所有签名将成为内部函数。

这是一个小问题,但它也可能在某些时候导致无法检测到的错误。幸运的是,标准库为它提供了解决方案。在定义装饰器时,您只需要用另一个装饰器来装饰内部函数。

听起来有点曲折,但它实际上增加了一行代码:

在此处理之后,装饰器不会影响它装饰的功能。

装饰器是功能对象的高级应用程序。在编写装饰器的过程中,您经常会遇到内部函数需要修改外部函数变量的情况。就像下面的装饰者一样:

为了计算函数调用的数量,我们需要修改函数内部外部函数定义的变量的值。但是,上面的代码是有问题的,解释器在执行它时会报告错误:

此错误是由与函数嵌套的作用域引起的。执行解释器时,不知道外部作用域中定义的变量。它被视为局部变量,并在当前范围中查找。最后,我没有找到关于变量的任何定义,然后抛出错误。为了解决这个问题,我们需要通过关键字告诉解释器:“count变量不属于当前的局部范围,去看外面。”之前的错误可以解决。

提示:有关非本地关键字历史的更多信息,请查看PEP-3104

在本文中,我与您分享了有关装饰器的一些提示和技巧。

总结了一些要点:

所有可调用对象都可用于实现装饰器

混合函数和类以更好地实现装饰器

wrapt模块非常有用,它可以帮助我们用更简单的代码编写复杂的装饰器

“装饰者”只是语法糖,它不是“装饰模式”

装饰者将改变函数的原始签名,你需要

当内部函数修改外部函数的变量时,需要使用关键字

看完文章后,你想呕吐吗?请在Project Github Issues上留言或告诉我。

回复以下“关键字”以获得高质量的资源

回复关键字“pybook03”,并立即与您的朋友一起获取主页翻译的《Think Python 2e》电子版

回复关键字“pybooks02”并从O'Reilly Press获得免费的Python相关电子书集

回复关键词“Book List 02”,并立即获得主页编译的10个Python引物的电子版。

豆瓣9.1分,中文版销售超过30万,零基础也可以用这本书学习Python

您想要的IT资源可能有

Python将超越C,Java并成为最流行的语言

如何编写优雅的Python函数?

标题地图:pexels,CC0授权。

——