- 第 1 章 安装 Python
- 1.2. Windows 上的 Python
- 1.3. Mac OS X 上的 Python
- 1.4. Mac OS 9 上的 Python
- 1.5. RedHat Linux 上的 Python
- 1.6. Debian GNU/Linux 上的 Python
- 1.7. 从源代码安装 Python
- 1.8. 使用 Python 的交互 Shell
- 1.9. 小结
- 第 2 章 第一个 Python 程序
- 2.2. 函数声明
- 2.3. 文档化函数
- 2.4. 万物皆对象
- 2.5. 代码缩进
- 2.6. 测试模块
- 第 3 章 内置数据类型
- 3.2. List 介绍
- 3.3. Tuple 介绍
- 3.4. 变量声明
- 3.5. 格式化字符串
- 3.6. 映射 list
- 3.7. 连接 list 与分割字符串
- 3.8. 小结
- 第 4 章 自省的威力
- 4.2. 使用可选参数和命名参数
- 4.3. 使用 type、str、dir 和其它内置函数
- 4.4. 通过 getattr 获取对象引用
- 4.5. 过滤列表
- 4.6. and 和 or 的特殊性质
- 4.7. 使用 lambda 函数
- 4.8. 全部放在一起
- 4.9. 小结
- 第 5 章 对象和面向对象
- 5.2. 使用 from module import 导入模块
- 5.3. 类的定义
- 5.4. 类的实例化
- 5.5. 探索 UserDict: 一个封装类
- 5.6. 专用类方法
- 5.7. 高级专用类方法
- 5.8. 类属性介绍
- 5.9. 私有函数
- 5.10. 小结
- 第 6 章 异常和文件处理
- 6.2. 与文件对象共事
- 6.3. for 循环
- 6.4. 使用 sys.modules
- 6.5. 与 Directory 共事
- 6.6. 全部放在一起
- 6.7. 小结
- 第 7 章 正则表达式
- 7.2. 个案研究:街道地址
- 7.3. 个案研究:罗马字母
- 7.4. 使用{n,m} 语法
- 7.5. 松散正则表达式
- 7.6. 个案研究: 解析电话号码
- 7.7. 小结
- 第 8 章 HTML 处理
- 8.2. sgmllib.py 介绍
- 8.3. 从 HTML 文档中提取数据
- 8.4. BaseHTMLProcessor.py 介绍
- 8.5. locals 和 globals
- 8.6. 基于 dictionary 的字符串格式化
- 8.7. 给属性值加引号
- 8.8. dialect.py 介绍
- 8.9. 全部放在一起
- 8.10. 小结
- 第 9 章 XML 处理
- 9.2. 包
- 9.3. XML 解析
- 9.4. Unicode
- 9.5. 搜索元素
- 9.6. 访问元素属性
- 9.7. Segue
- 第 10 章 Scripts 和 Streams
- 10.2. 标准输入、输出和错误
- 10.3. 缓冲节点查询
- 10.4. 查找节点的直接子节点
- 10.5. 通过节点类型创建独立的处理句柄 Creating separate handlers by node type
- 10.6. 处理命令行参数
- 10.7. 全部放在一起
- 10.8. 小结
- 第 11 章 HTTP Web 服务
- 11.2. 避免通过 HTTP 重复地获取数据
- 11.3. HTTP 的特性
- 11.4. 调试 HTTP web 服务
- 11.5. 设置 User-Agent
- 11.6. 处理 Last-Modified 和 ETag
- 11.7. 处理重定向
- 11.8. 处理被压缩的数据
- 11.9. 全部放在一起
- 11.10. 小结
- 第 12 章 SOAP Web 服务
- 12.2. 安装 SOAP 库
- 12.3. 步入 SOAP
- 12.4. SOAP 网络服务查错
- 12.5. WSDL 介绍
- 12.6. 以 WSDL 进行 SOAP 内省
- 12.7. 搜索 Google
- 12.8. SOAP 网络服务故障排除
- 12.9. 小结
- 第 13 章 单元测试
- 13.2. 深入
- 13.3. 介绍 romantest.py
- 13.4. 正面测试(Testing for success)
- 13.5. 负面测试(Testing for failure)
- 13.6. 完备性检测(Testing for sanity)
- 第 14 章 以测试优先为原则的编程
- 14.2. roman.py, 第 2 阶段
- 14.3. roman.py, 第 3 阶段
- 14.4. roman.py, 第 4 阶段
- 14.5. roman.py, 第 5 阶段
- 第 15 章 重构
- 15.2. 应对需求变化
- 15.3. 重构
- 15.4. 后记
- 15.5. 小结
- 第 16 章 有效编程(Functional Programming)
- 16.2. 找到路径
- 16.3. 过滤已访问列表
- 16.4. 关联已访问列表
- 16.5. 数据中心思想编程
- 16.6. 动态导入模块
- 16.7. 全部放在一起
- 16.8. 小结
- 第 17 章 动态函数
- 17.2. plural.py, 第 1 阶段
- 17.3. plural.py, 第 2 阶段
- 17.4. plural.py, 第 3 阶段
- 17.5. plural.py, 第 4 阶段
- 17.6. plural.py, 第 5 阶段
- 17.7. plural.py, 第 6 阶段
- 17.8. 小结
- 第 18 章 性能优化
- 18.2. 使用 timeit 模块
- 18.3. 优化正则表达式
- 18.4. 优化字典查找
- 18.5. 优化列表操作
- 18.6. 优化字符串操作
- 18.7. 小结
- 附录 A. 进一步阅读
- 附录 B. 五分钟回顾
- 附录 C. 技巧和窍门
- 附录 D. 示例清单
- 附录 E. 修订历史
- 附录 F. 关于本书
- 附录 G. GNU Free Documentation License
- G.1. Applicability and definitions
- G.2. Verbatim copying
- G.3. Copying in quantity
- G.4. Modifications
- G.5. Combining documents
- G.6. Collections of documents
- G.7. Aggregation with independent works
- G.8. Translation
- G.9. Termination
- G.10. Future revisions of this license
- G.11. How to use this License for your documents
- 附录 H. Python license
- H.B. Terms and conditions for accessing or otherwise using Python
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
5.5. 探索 UserDict: 一个封装类
5.5. 探索 UserDict: 一个封装类
如你所见, FileInfo 是一个象字典一样动作的类。为了进一步揭示这一点,让我们看一看在 UserDict 模块中的 UserDict 类,它是我们的 FileInfo 类的父类。它没有什么特别的,也是用 Python 写的,并且保存在一个 .py 文件里,就象我们其他的代码。特别之处在于,它保存在你的 Python 安装目录的 lib 目录下。
在 Windows 下的 ActivePython IDE 中,你可以快速打开在你的库路径中的任何模块,使用 File->Locate... (Ctrl-L)。 |
例 5.9. 定义 UserDict 类
class UserDict: def __init__(self, dict=None): self.data = {} if dict is not None: self.update(dict)
注意 UserDict 是一个基类,不是从任何其他类继承而来。 | |
这就是我们 在 FileInfo 类中进行了覆盖 的 __init__ 方法。注意这个父类的参数列表与子类不同。很好,每个子类可以拥有自已的参数集,只要使用正确的参数调用父类就可以了。这里父类有一个定义初始值的方法(通过在 dict 参数中传入一个字典),这一方法我们的 FileInfo 没有用上。 | |
Python 支持数据属性(在 Java 和 Powerbuilder 中叫做 “实例变量”,在 C++ 中叫 “数据成员”),它是由某个特定的类实例所拥有的数据。在本例中,每个 UserDict 实例将拥有一个 data 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,instance.data,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使用 self 作为限定符。习惯上,所有的数据属性都在 __init__ 方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,象局部变量一样,当你首次赋给它值的时候突然产生。 | |
update 方法是一个字典复制器:它把一个字典中的键和值全部拷贝到另一个字典。 这个操作 并不 事先清空目标字典,如果一些键在目标字典中已经存在,则它们将被覆盖,那些键名在目标字典中不存在的则不改变。应该把 update 看作是合并函数,而不是复制函数。 | |
这个语法你可能以前没看过(我还没有在这本书中的例子中用过它)。这是一条 if 语句,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就象在 C++ 中没有用大括号包括的单行语句。) 你可以用这种语法,或者可以在后面的行拥有缩近代码,但是不能对同一个块同时用两种方式。 |
Java 和 Powerbuilder 支持通过参数列表的重载,也就是 一个类可以有同名的多个方法,但这些方法或者是参数个数不同,或参数的类型不同。其它语言(最明显如 PL/SQL)甚至支持通过参数名的重载,也就是 一个类可以有同名的多个方法,这些方法有相同类型,相同个数的参数,但参数名不同。Python 两种都不支持,总之是没有任何形式的函数重载。一个 __init__ 方法就是一个 __init__ 方法,不管它有什么样的参数。每个类只能有一个 __init__ 方法,并且如果一个子类拥有一个 __init__ 方法,它 总是 覆盖父类的 __init__ 方法,甚至子类可以用不同的参数列表来定义它。 |
Python 的原作者 Guido 是这样解释方法覆盖的 “子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,在调用同一对象的另外方法时,父类中一个方法对另一个同类中的方法的调用,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。(对于 C++ 程序员,所有的 Python 方法都非常有效)” 如果你不明白(它另我颇感困惑),不必在意。我想我要跳过它。 |
应该总是在 __init__ 方法中给一个实例的所有数据属性赋予一个初始值。这样做将会节省你在后面调试的时间,不必为捕捉因使用未初始化(也就是不存在)的属性而导致的 AttributeError 异常费时费力。 |
例 5.10. UserDict 常规方法
def clear(self): self.data.clear() def copy(self): if self.__class__ is UserDict: return UserDict(self.data) import copy return copy.copy(self) def keys(self): return self.data.keys() def items(self): return self.data.items() def values(self): return self.data.values()
clear 是一个普通的类方法,可以在任何时候被任何人公开调用。注意,clear 象所有的类方法一样(常规的或专用的),使用 self 作为它的第一个参数。(记住,当你调用方法时,不用包括 self;这件事是 Python 替你做的。) 还应注意这个封装类的基本技术:将一个真正的字典 (data) 作为数据属性保存起来,定义所有真正字典所拥有的方法,并且将每个类方法重定向到真正字典上的相应方法。(在你忘记的情况下,字典的 clear 方法 删除它的所有关键字 和关键字相应的值。) | |
真正字典的 copy 方法会返回一个新的字典,它是原始字典的原样的复制(所有的键-值对都相同)。但是 UserDict 不能简单地重定向到 self.data.copy,因为那个方法返回一个真正的字典,而我们想要的是返回同一个类的一个新的实例,就象是 self。 | |
我们使用 __class__ 属性来查看是否 self 是一个 UserDict,如果是,太好了,因为我们知道如何拷贝一个 UserDict:只要创建一个新的 UserDict ,并传给它真正的字典,这个字典已经存放在 self.data 中了。 然后你立即返回这个新的 UserDict,你甚至于不需要再下面一行中使用 import copy。 | |
如果 self.__class__ 不是 UserDict,那么 self 一定是 UserDict 的某个子类(如可能为 FileInfo),生活总是存在意外。 UserDict 不知道如何生成它的子类的一个原样的拷贝,例如,有可能在子类中定义了其它的数据属性,所以我们只能完全复制它们,确定拷贝了它们的全部内容。幸运的是,Python 带了一个模块可以正确地完成这件事,它叫做 copy。在这里我不想深入细节(然而它是一个绝对酷的模块,是否你想到要自已研究它了呢?)。说 copy 能够拷贝任何 Python 对象就够了,这就是为什么我们在这里用它的原因。 | |
其余的方法是直截了当的重定向到 self.data 的内置函数上。 |
在 Python 2.2 之前的版本中,你不可以直接子类化字符串、列表以及字典之类的内建数据类型。 作为补偿, Python 提供封装类来模拟内建数据类型的行为,比如:UserString, UserList 和 UserDict。 通过混合使用普通和特殊方法, UserDict 类出色于模仿字典。 在 Python 2.2 和其后的版本中,你可以直接从 dict 内建数据类型继承。本书 fileinfo_fromdict.py 中有这方面的一个例子。 |
如例子中所示,在 Python 中,你可以直接继承自内建数据类型 dict,这样做有三点与 UserDict 不同。
例 5.11. 直接继承自内建数据类型 dict
class FileInfo(dict): "store file metadata" def __init__(self, filename=None): self["name"] = filename
第一个区别是你不需要导入 UserDict 模块,因为 dict 是已经可以使用的内建数据类型。第二个区别是你不是继承自 UserDict.UserDict ,而是直接继承自 dict。 | |
第三个区别有些晦涩,但却很重要。 UserDict 内部的工作方式要求你手工地调用它的 __init__ 方法去正确初始化它的内部数据结构。 dict 并不这样工作,它不是一个封装所以不需要明确的初始化。 |
进一步阅读
- Python Library Reference 提供了 UserDict 模块 和 copy 模块 的文档。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论