- 译者序
- 前言
- 第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.6和Python 3.0下运行一段脚本的时候,这个版本的输出如下所示。注意spam和eggs函数的每一个是如何有自己的调用计数器的,因为每个装饰都创建一个新的类实例:
尽管对于装饰函数有用,但是当应用于方法的时候,这种编码方案也有问题(随后更为详细地介绍)。
封闭作用域和全局作用域
封闭def作用域引用和嵌套的def常常可以实现相同的效果,特别是对于装饰的最初函数这样的静态数据。然而,在这个例子中,我们也需要封闭的作用域中的一个计数器,它随着每次调用而更改,并且,这在Python 2.6中是不可能的。在Python 2.6中,我们可以使用类和属性,正如我们前面所做的那样,或者使用全局声明把状态变量移出到全局作用域:
遗憾的是,把计数器移出到共同的全局作用域允许像这样修改它们,也意味着它们将为每个包装的函数所共享。和类实例属性不同,全局计数器是跨程序的,而不是针对每个函数的——对于任何跟踪的函数调用,计数器都会递增。如果你比较这个版本与前一个版本的输出,就可以看出其中的区别——单个的、共享的全局调用计数器根据每次装饰函数的调用不正确地更新:
封闭作用域和nonlocal
共享全局状态可能是我们在某些情况下想要的。如果我们真的想要一个针对每个函数的计数器,要么像前面那样使用类,要么使用Python 3.0中新的nonlocal语句,第17章曾介绍过该语句。由于这一新的语句允许修改封闭的函数作用域变量,所以它们可以充当针对每次装饰的、可修改的数据:
现在,由于封装的作用域变量不能跨程序而成为全局的,所以每个包装的函数再次有了自己的计数器,就像是针对类和属性一样。这里是在Python 3.0下运行时新的输出:
函数属性
最后,如果你没有使用Python 3.X并且没有一条nonlocal语句,可能仍然能够针对某些可改变的状态使用函数属性来避免全局和类。在最新的Pythons中,我们可以把任意属性分配给函数以附加它们,使用func.attr=value就可以了。在我们的例子中,可以直接对状态使用wrapper.calls。如下的代码与前面的nonlocal版本一样地工作,因为计数器再一次是针对每个装饰的函数的,但是,它也可以在Python 2.6下运行:
注意,这种方法有效,只是因为名称wrapper保持在封闭的tracer函数的作用域中。当我们随后增加wrapper.calls时,并不是在修改名称wrapper本身,因此,不需要nonlocal声明。
这种方案几乎作为一个脚注来介绍,因为它比Python 3.0中的nonlocal要隐晦得多,并且可能留待其他方案无济于事的情况下使用更好。然而,我们将解答本章末尾一个问题的时候使用它,那里,我们需要从装饰器代码的外部访问保存的状态;nonlocal只能从嵌套函数自身的内部看到,但是函数属性有更广泛的可见性。
由于装饰器往往意味着可调用对象的多个层级,所以我们可以用封闭的作用域和带有属性的类来组合函数,以实现各种各样的编码结构。正如我们随后将见到的,这有时候可能比我们所期待的要细微——每个装饰的函数应该有自己的状态,并且每个装饰的类都应该需要针对自己的状态和针对每个产生实例的状态。
实际上,正如下一小节所介绍的,如果我们也想要对一个类方法应用函数装饰器,必须小心Python在作为可调用类实例对象的装饰器编码和作为函数的装饰器编码之间的区分。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论