返回介绍

基础知识

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

让我们首先从一个符号的角度来第一次看一看装饰行为。我们很快将编写真正的代码,但是,由于装饰器的很多神奇之处可归结为自动重绑定操作,所以首先理解这一映射是很重要的。

函数装饰器

函数装饰器已经从Python 2.5开始可用。正如我们在本书前面所见到的,它们主要只是一种语法糖:通过在一个函数的def语句的末尾来运行另一个函数,把最初的函数名重新绑定到结果。

用法

函数装饰器是一种关于函数的运行时声明,函数的定义需要遵守此声明。装饰器在紧挨着定义一个函数或方法的def语句之前的一行编写,并且它由@符号以及紧随其后的对于元函数的一个引用组成——这是管理另一个函数的一个函数(或其他的可调用对象)。

在编码方面,函数装饰器自动将如下的语法:

映射为这一对等的形式,其中装饰器是一个单参数的可调用对象,它返回与F具有相同数目的参数的一个可调用对象:

这一自动名称重绑定在def语句上有效,不管它针对一个简单的函数或是类中的一个方法。当随后调用F函数的时候,它自动调用装饰器所返回的对象,该对象可能是实现了所需的包装逻辑的另一个对象,或者是最初的函数本身。

换句话说,装饰实际把如下的第一行映射为第二行(尽管装饰器实际上只运行一次,在装饰的时候):

这一自动名称重绑定说明了我们在本书前面遇到的静态方法和正确的装饰语法的原因:

在这两个例子中,在def语句的末尾,方法名重新绑定到一个内置函数装饰器的结果。随后再调用最初的名称,将会调用装饰器所返回的对象。

实现

装饰器自身是一个返回可调用对象的可调用对象。也就是说,它返回了一个对象,当随后装饰的函数通过其最初的名称调用的时候,将会调用这个对象——不管是拦截了随后调用的一个包装器对象,还是最初的函数以某种方式的扩展。实际上,装饰器可以是任意类型的可调用对象,并且返回任意类型的可调用对象:函数和类的任何组合都可以使用,尽管一些组合更适合于特定的背景。

例如,要在一个函数创建之后接入装饰协议以管理函数,我们需要编写如下形式的装饰器:

由于最初的装饰函数分配回给其名称,这么做将直接向函数的定义添加创建之后的步骤。这样的一个结构可能会用来把一个函数注册到一个API、赋值函数属性,等等。

更典型的用法,是插入逻辑以拦截对函数的随后调用,我们可以编写一个装饰器来返回和最初函数不同的一个对象:

这个装饰器在装饰的时候调用,并且当随后调用最初的函数名的时候,它所返回的调用对象将被调用。装饰器自身接受被装饰的函数,返回的调用对象会接受随后传递给被装饰函数的名称的任何参数。这和类方法的工作方式相同:隐含的实例对象只是在返回的可调用对象的第一个参数中出现。

更概括地说,有一种常用的编码模式可以包含这一思想——装饰器返回了一个包装器,包装器把最初的函数保持到一个封闭的作用域中:

当随后调用名称func的时候,它确实调用装饰器所返回的包装器函数;随后包装器函数可能会运行最初的func,因为它在一个封闭的作用域中仍然可以使用。当以这种方式编码的时候,每个装饰的函数都会产生一个新的作用域来保持状态。

为了对类做同样的事情,我们可以重载调用操作,并且使用实例属性而不是封闭的作用域:

现在,随后再调用func的时候,它确实会调用装饰器所创建的实例的__call__运算符重载方法;然后,__call__方法可能运行最初的func,因为它在一个实例属性中仍然可用。当按照这种方式编写代码的时候,每个装饰的函数都会产生一个新的实例来保持状态。

支持方法装饰

关于前面的基于类的代码的细微的一点是,尽管它对于拦截简单函数调用有效,但当它应用于类方法函数的时候,并不是很有效:

当按照这种方式编码的时候,装饰的方法重绑定到装饰器类的一个实例,而不是一个简单的函数。

这一点带来的问题是,当装饰器的__call__方法随后运行的时候,其中的self接收装饰器类实例,并且类C的实例不会包含到一个*args中。这使得有可能把调用分派给最初的方法——即保持了最初的方法函数的装饰器对象,但是,没有实例传递给它。

为了支持函数和方法,嵌套函数的替代方法工作得更好:

当按照这种方法编写的包装类在其第一个参数里接收了C类实例的时候,它可以分派到最初的方法和访问状态信息。

从技术上讲,这种嵌套函数版本是有效的,因为Python创建了一个绑定的方法对象,并且由此只有当一个方法属性引用一个简单的函数的时候,才把主体类实例传递给self参数;相反,当它引用可调用的类的一个实例的时候,可调用的类的实例传递给self,以允许可调用的类访问自己的状态信息。在本章随后,我们还将看到这一细微的区别在实际实例中的作用。

还要注意,嵌套函数可能是支持函数和方法的装饰的最直接方式,但是不一定是唯一的方式。例如,上一章中的描述符,调用的时候接收了描述符和主体类实例。然而,更为复杂的是,在本章稍后,我们将看到这一工具如何在这一背景下起作用。

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

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

发布评论

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