# 5.3. 类的定义
* 5.3.1\. 初始化并开始类编码
* 5.3.2\. 了解何时去使用 self 和 __init__
Python 是完全面向对象的:你可以定义自已的类,从自已的或内置的类继承,然后从你定义的类创建实例。
在 Python 中定义类很简单。就像定义函数,没有单独的接口定义。只要定义类,然后就可以开始编码。Python 类以保留字 `class` 开始,后面跟着类名。从技术上讲,有这些就够了,因为一个类并非必须从其它类继承。
## 例 5.3. 最简单的 Python 类
```
class Loaf:
pass
```
| | |
| --- | --- |
| \[1\] | 这个类的名字是 `Loaf`,它没有从其它类继承。类名通常是第一个字母大写,如:`EachWordLikeThis`,但这只是一个习惯,不是一个必要条件。 |
| \[2\] | 这个类没有定义任何方法或属性,但是从语法上,需要在定义中有些东西,所以你使用 `pass`。这是一个 Python 保留字,仅仅表示 “向前走,不要往这看”。它是一条什么都不做的语句,当你删空函数或类时,它是一个很好的占位符。 |
| \[3\] | 你可能猜到了,在类中的所有东西都要缩近,就像位于函数、`if` 语句,`for` 循环,诸如此类的代码。第一条不缩近的东西不属于这个类。 |
> 注意
> 在 Python 中的 `pass` 语句就像 Java 或 C 中的大括号空集 (`{}`)。
当然,实际上大多数的类都是从其它的类继承来的,并且它们会定义自已的类方法和属性。但是就像你刚才看到的,除了名字以外,类没有什么必须要具有的。特别是,C++ 程序员可能会感到奇怪,Python 的类没有显示的构造函数和析构函数。Python 类的确存在与构造函数相似的东西:`__init__` 方法。
## 例 5.4. 定义 `FileInfo` 类
```
from UserDict import UserDict
class FileInfo(UserDict):
```
| | |
| --- | --- |
| \[1\] | 在 Python 中,类的基类只是简单地列在类名后面的小括号里。所以 `FileInfo` 类是从 `UserDict` 类 (它是从 [`UserDict` 模块导进来的](importing_modules.html "5.2. 使用 from module import 导入模块")) 继承来的。`UserDict` 是一个像字典一样工作的类,它允许你完全子类化字典数据类型,同时增加你自已的行为。{也存在相似的类 `UserList` 和 `UserString` ,它们允许你子类化列表和字符串。)\[2\] 在这个类的背后有一些“巫术”,我们将在本章的后面,随着更进一步地研究 `UserDict` 类,揭开这些秘密。 |
> 注意
> 在 Python 中,类的基类只是简单地列在类名后面的小括号里。不像在 Java 中有一个特殊的 `extends` 关键字。
Python 支持多重继承。在类名后面的小括号中,你可以列出许多你想要的类名,以逗号分隔。
## 5.3.1. 初始化并开始类编码
本例演示了使用 `__init__` 方法来进行 `FileInfo` 类的初始化。
## 例 5.5. 初始化 `FileInfo` 类
```
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
```
| | |
| --- | --- |
| \[1\] | 类也可以 (并且[应该](../getting_to_know_python/documenting_functions.html#tip.docstring)) 有 `doc string`s ,就像方法和函数一样。 |
| \[2\] | `__init__` 在类的实例创建后被立即调用。它可能会引诱你称之为类的构造函数,但这种说法并不正确。说它引诱,是因为它看上去像 (按照习惯,`__init__` 是类中第一个定义的方法),行为也像 (在一个新创建的类实例中,它是首先被执行的代码),并且叫起来也像 (“init”当然意味着构造的本性)。说它不正确,是因为对象在调用 `__init__` 时已经被构造出来了,你已经有了一个对类的新实例的有效引用。但 `__init__` 是在 Python 中你可以得到的最接近构造函数的东西,并且它也扮演着非常相似的角色。 |
| \[3\] | 每个类方法的第一个参数,包括 `__init__`,都是指向类的当前实例的引用。按照习惯这个参数总是被称为 `self`。在 `__init__` 方法中,`self` 指向新创建的对象;在其它的类方法中,它指向方法被调用的类实例。尽管当定义方法时你需要明确指定 `self`,但在调用方法时,你_不_ 用指定它,Python 会替你自动加上的。 |
| \[4\] | `__init__` 方法可以接受任意数目的参数,就像函数一样,参数可以用缺省值定义,即可以设置成对于调用者可选。在本例中,`filename` 有一个缺省值 `None`,即 Python 的空值。 |
> 注意
> 习惯上,任何 Python 类方法的第一个参数 (对当前实例的引用) 都叫做 `self`。这个参数扮演着 C++ 或 Java 中的保留字 `this` 的角色,但 `self` 在 Python 中并不是一个保留字,它只是一个命名习惯。虽然如此,也请除了 `self` 之外不要使用其它的名字,这是一个非常坚固的习惯。
## 例 5.6. 编写 `FileInfo` 类
```
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
```
| | |
| --- | --- |
| \[1\] | 一些伪面向对象语言,像 Powerbuilder 有一种“扩展”构造函数和其它事件的概念,即父类的方法在子类的方法执行前被自动调用。Python 不是这样,你必须显示地调用在父类中的合适方法。 |
| \[2\] | 我告诉过你,这个类像字典一样工作,那么这里就是第一个印象。我们将参数 `filename` 赋值给对象 `name` 关键字,作为它的值。 |
| \[3\] | 注意 `__init__` 方法从不返回一个值。 |
## 5.3.2. 了解何时去使用 `self` 和 `__init__`
当定义你自已的类方法时,你_必须_ 明确将 `self` 作为每个方法的第一个参数列出,包括 `__init__`。当从你的类中调用一个父类的一个方法时,你必须包括 `self` 参数。但当你从类的外部调用你的类方法时,你不必对 `self` 参数指定任何值;你完全将其忽略,而 Python 会自动地替你增加实例的引用。我知道刚开始这有些混乱,它并不是自相矛盾的,因为它依靠于一个你还不了解的区别 (在绑定与非绑定方法之间),故看上去是矛盾的。
噢。我知道有很多知识需要吸收,但是你要掌握它。所有的 Python 类以相同的方式工作,所以一旦你学会了一个,就是学会了全部。如果你忘了别的任何事,也要记住这件事,因为我认定它会让你出错:
> 注意
> `__init__` 方法是可选的,但是一旦你定义了,就必须记得显示调用父类的 `__init__` 方法 (如果它定义了的话)。这样更是正确的:无论何时子类想扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显式调用父类方法。
## 进一步阅读关于 Python 类
* _Learning to Program_ 有优雅的[类的介绍](http://www.freenetpages.co.uk/hp/alan.gauld/tutclass.htm)。
* _How to Think Like a Computer Scientist_ 展示了如何[使用类来实现复合数据类型模型](http://www.ibiblio.org/obp/thinkCSpy/chap12.htm)。
* _Python Tutorial_ 深入考虑了[类、名字空间和继承](http://www.python.org/doc/current/tut/node11.html)。
* Python Knowledge Base 回答了[关于类的常见问题](http://www.faqts.com/knowledge-base/index.phtml/fid/242)。
## Footnotes
\[2\] 在 2.2 之后已经可以从 dict、list 来派生子类了,关于这一点作者在后文也会提到。――译注
- 版权信息
- 第 1 章 安装 Python
- 1.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.1. 概览
- 2.2. 函数声明
- 2.3. 文档化函数
- 2.4. 万物皆对象
- 2.5. 代码缩进
- 2.6. 测试模块
- 第 3 章 内置数据类型
- 3.1. Dictionary 介绍
- 3.2. List 介绍
- 3.3. Tuple 介绍
- 3.4. 变量声明
- 3.5. 格式化字符串
- 3.6. 映射 list
- 3.7. 连接 list 与分割字符串
- 3.8. 小结
- 第 4 章 自省的威力
- 4.1. 概览
- 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.1. 概览
- 5.2. 使用 from _module_ import 导入模块
- 5.3. 类的定义
- 5.4. 类的实例化
- 5.5. 探索 UserDict:一个封装类
- 5.6. 专用类方法
- 5.7. 高级专用类方法
- 5.8. 类属性介绍
- 5.9. 私有函数
- 5.10. 小结
- 第 6 章 异常和文件处理
- 6.1. 异常处理
- 6.2. 与文件对象共事
- 6.3. for 循环
- 6.4. 使用 `sys.modules`
- 6.5. 与目录共事
- 6.6. 全部放在一起
- 6.7. 小结
- 第 7 章 正则表达式
- 7.1. 概览
- 7.2. 个案研究:街道地址
- 7.3. 个案研究:罗马字母
- 7.4. 使用 {n,m} 语法
- 7.5. 松散正则表达式
- 7.6. 个案研究:解析电话号码
- 7.7. 小结
- 第 8 章 HTML 处理
- 8.1. 概览
- 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.1. 概览
- 9.2. 包
- 9.3. XML 解析
- 9.4. Unicode
- 9.5. 搜索元素
- 9.6. 访问元素属性
- 9.7. Segue [9]
- 第 10 章 脚本和流
- 10.1. 抽象输入源
- 10.2. 标准输入、输出和错误
- 10.3. 查询缓冲节点
- 10.4. 查找节点的直接子节点
- 10.5. 根据节点类型创建不同的处理器
- 10.6. 处理命令行参数
- 10.7. 全部放在一起
- 10.8. 小结
- 第 11 章 HTTP Web 服务
- 11.1. 概览
- 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.1. 概览
- 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.1. 罗马数字程序介绍 II
- 13.2. 深入
- 13.3. romantest.py 介绍
- 13.4. 正面测试 (Testing for success)
- 13.5. 负面测试 (Testing for failure)
- 13.6. 完备性检测 (Testing for sanity)
- 第 14 章 测试优先编程
- 14.1. roman.py, 第 1 阶段
- 14.2. roman.py, 第 2 阶段
- 14.3. roman.py, 第 3 阶段
- 14.4. roman.py, 第 4 阶段
- 14.5. roman.py, 第 5 阶段
- 第 15 章 重构
- 15.1. 处理 bugs
- 15.2. 应对需求变化
- 15.3. 重构
- 15.4. 后记
- 15.5. 小结
- 第 16 章 函数编程
- 16.1. 概览
- 16.2. 找到路径
- 16.3. 重识列表过滤
- 16.4. 重识列表映射
- 16.5. 数据中心思想编程
- 16.6. 动态导入模块
- 16.7. 全部放在一起
- 16.8. 小结
- 第 17 章 动态函数
- 17.1. 概览
- 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.1. 概览
- 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.0. Preamble
- 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. GNU 自由文档协议
- H.0. 序
- H.1. 适用范围和定义
- H.2. 原样复制
- H.3. 大量复制
- H.4. 修改
- H.5. 合并文档
- H.6. 文档合集
- H.7. 独立著作聚集
- H.8. 翻译
- H.9. 终止协议
- H.10. 协议将来的修订
- H.11. 如何为你的文档使用本协议
- 附录 I. Python license
- I.A. History of the software
- I.B. Terms and conditions for accessing or otherwise using Python
- 附录 J. Python 协议
- J.0. 关于译文的声明
- J.A. 软件的历史
- J.B. 使用 Python 的条款和条件