- 译者序
- 前言
- 第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 各部分练习题的解答
- 作者介绍
- 封面介绍
跟踪对象接口
例如,在第30章中,__getattr__运算符重载方法作为包装嵌入的实例的整个对象接口的一种方法,以便实现委托编码模式。我们在前一章介绍的管理的属性中看到过类似的例子。还记得吧,当获取未定义的属性名的时候,__getattr__会运行;我们可以使用这个钩子来拦截一个控制器类中的方法调用,并将它们传递给一个嵌入的对象。
为了便于参考,这里给出最初的非装饰器委托示例,它在两个内置类型对象上工作:
在这段代码中,Wrapper类拦截了对任何包装的对象的属性的访问,打印出一条跟踪信息,并且使用内置函数getattr来终止对包装对象的请求。它特别跟踪包装的对象的类之外发出的属性访问。在包装的对象内部访问其方法不会被捕获,并且会按照设计正常运行。这种整体接口模型和函数装饰器的行为不同,装饰器只包装一个特定的方法。
类装饰器为编写这种__getattr__技术来包装一个完整接口提供了一个替代的、方便的方法。例如,在Python 2.6和Python 3.0中,前面的类示例可能编写为一个类装饰器,来触发包装的实例创建,而不是把一个预产生的实例传递到包装器的构造函数中(在这里也用**kargs扩展了,以支持关键字参数,并且统计进行访问的次数):
这里与我们前面在“编写函数装饰器”一节中遇到的跟踪器装饰器有很大不同,注意到这点很重要,在那里,我们看到了装饰器可以使我们跟踪和计时对一个给定函数或方法的调用。相反,通过拦截实例创建调用,这里的类装饰器允许我们跟踪整个对象接口,例如,对其任何属性的访问。
下面是这段代码在Python 2.6和Python 3.0下的输出:Spam和Person类的实例上的属性获取都会调用Wrapper类中的__getattr__逻辑,由于food和bob确实都是Wrapper的实例,得益于装饰器的实例创建调用重定向:
注意,前面的代码装饰了一个用户定义的类。就像是在本书第30章最初的例子中一样,我们也可以使用装饰器来包装一个内置的类型,例如列表,只要我们的子类允许装饰器语法,或者手动地执行装饰——装饰器语法对于@行需要一条class语句。
在下面的代码中,由于装饰的间接作用,x实际是一个Wrapper(我把装饰器类放到了模块文件tracer.py中,以便以这种方式重用它):
这种装饰器方法允许我们把实例创建移动到装饰器自身之中,而不是要求传入一个预先生成的对象。尽管这好像是一个细小的差别,它允许我们保留常规的实例创建语法并且通常实现装饰器的所有优点。我们只需要用装饰器语法来扩展类,而不是要求所有的实例创建调用都通过一个包装器来手动地指向对象:
假设你将会产生类的多个实例,装饰器通常将会在代码大小和代码可维护性上双赢。
注意:属性版本差异:正如我们在本书第37章所了解到的,在Python 2.6中,__getattr__将会拦截对__str__和__repr__这样的运算符重载方法的访问,但是,在Python 3.0中不会这样。
在Python 3.0中,类实例会从类中继承这些方法中的一些(而不是全部)的默认形式(实际上,是从自动对象超类),因为所有的类都是“新式的”。此外,在Python 3.0中,针对打印和+这样的内置操作显式地调用属性并不会通过__getattr__(或其近亲__getattribute__)路由。新式类在类中查找这样的方法,并且完全省略常规的实例查找。
此处意味着,在Python 2.6中,基于__getattr__的跟踪包装器将会自动跟踪和传递运算符重载,但是,在Python 3.0中不会如此。要看到这一点,直接在交互式会话的前面的末尾显示"x",在Python 2.6中,属性__repr__被跟踪并且该列表如预期的那样打印出来,但是在Python 3.0中,不会发生跟踪并且列表打印为Wrapper类使用一个默认显示:
要在Python 3.0中同样工作,运算符重载方法通常需要在包装类中冗余地重新定义,要么手动定义,要么通过工具定义,或者通过在超类中定义。只有简单命名的属性会在两种版本中都同样工作。我们将在本章稍后的一个Private装饰器中再次看到版本差异的作用。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论