- 译者序
- 前言
- 第1章 问答环节
- 第2章 Python 如何运行程序
- 第3章 如何运行程序
- 第4章 介绍 Python 对象类型
- 第5章 数字
- 第6章 动态类型简介
- 第7章 字符串
- 第8章 列表与字典
- 第9章 元组、文件及其他
- 第10章 Python 语句简介
- 第11章 赋值、表达式和打印
- 第12章 if 测试和语法规则
- 第13章 while 和 for 循环
- 第14章 迭代器和解析,第一部分
- 第15章 文档
- 第16章 函数基础
- 第17章 作用域
- 第18章 参数
- 第19章 函数的高级话题
- 第20章 迭代和解析,第二部分
- 第21章 模块:宏伟蓝图
- 第22章 模块代码编写基础
- 第23章 模块包
- 第24章 高级模块话题
- 第25章 OOP:宏伟蓝图
- 第27章 更多实例
- 第28章 类代码编写细节
- 第29章 运算符重载
- 第30章 类的设计
- 第31章 类的高级主题
- 第32章 异常基础
- 第34章 异常对象
- 第35章 异常的设计
- 第36章 Unicode 和字节字符串
- 字符串基础知识
- Python 的字符串类型
- 文本和二进制文件
- Python 3.0 中的字符串应用
- 转换
- 编码 Unicode 字符串
- 编码非ASCII文本
- 编码和解码非ASCII文本
- 其他 Unicode 编码技术
- 转换编码
- 在 Python 2.6 中编码 Unicode 字符串
- 源文件字符集编码声明
- 使用 Python 3.0 Bytes 对象
- 序列操作
- 创建 bytes 对象的其他方式
- 混合字符串类型
- 使用 Python 3.0(和 Python 2.6)bytearray 对象
- 使用文本文件和二进制文件
- Python 3.0 中的文本和二进制模式
- 类型和内容错误匹配
- 使用 Unicode 文件
- 在 Python 3.0 中处理 BOM
- Python 2.6 中的 Unicode 文件
- Python 3.0 中其他字符串工具的变化
- Struct二进制数据模块
- pickle对象序列化模块
- XML解析工具
- 本章小结
- 本章习题
- 习题解答
- 第37章 管理属性
- 第38章 装饰器
- 第39章 元类
- 附录A 安装和配置
- 附录B 各部分练习题的解答
- 作者介绍
- 封面介绍
基础知识
函数装饰器
函数装饰器已经从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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论