5.3. 类的定义
Description: Python from novice to pro
Keywords: Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free

# 深入 Python :Dive Into Python 中文版 Python 从新手到专家 [Dip_5.4b_CPyUG_Release] | Find:

## 5.3. 类的定义

Python 是完全面向对象的：你可以定义自已的类，从自已的或内置的类继承，然后从你定义的类创建实例。

在 Python 中定义类很简单。就像定义函数，没有单独的接口定义。只要定义类，然后就可以开始编码。Python 类以保留字 `class` 开始，后面跟着类名。从技术上讲，有这些就够了，因为一个类并非必须从其它类继承。

### 例 5.3. 最简单的 Python 类

    class Loaf:
        pass

| 这个类的名字是 `Loaf`，它没有从其它类继承。类名通常是第一个字母大写，如：`EachWordLikeThis`，但这只是一个习惯，不是一个必要条件。
| 这个类没有定义任何方法或属性，但是从语法上，需要在定义中有些东西，所以你使用 `pass`。这是一个 Python 保留字，仅仅表示 “向前走，不要往这看”。它是一条什么都不做的语句，当你删空函数或类时，它是一个很好的占位符。
| 你可能猜到了，在类中的所有东西都要缩近，就像位于函数、`if` 语句，`for` 循环，诸如此类的代码。第一条不缩近的东西不属于这个类。

在 Python 中的 `pass` 语句就像 Java 或 C 中的大括号空集 (`{}`)。

当然，实际上大多数的类都是从其它的类继承来的，并且它们会定义自已的类方法和属性。但是就像你刚才看到的，除了名字以外，类没有什么必须要具有的。特别是，C++ 程序员可能会感到奇怪，Python 的类没有显示的构造函数和析构函数。Python 类的确存在与构造函数相似的东西：`__init__` 方法。

### 例 5.4. 定义 `FileInfo` 类

    from UserDict import UserDict

    class FileInfo(UserDict):

| 在 Python 中，类的基类只是简单地列在类名后面的小括号里。所以 `FileInfo` 类是从 `UserDict` 类 (它是从 `UserDict` 模块导进来的) 继承来的。`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):

| 类也可以 (并且应该) 有 `doc string`s ，就像方法和函数一样。
| `__init__` 在类的实例创建后被立即调用。它可能会引诱你称之为类的构造函数，但这种说法并不正确。说它引诱，是因为它看上去像 (按照习惯，`__init__` 是类中第一个定义的方法)，行为也像 (在一个新创建的类实例中，它是首先被执行的代码)，并且叫起来也像 (“init”当然意味着构造的本性)。说它不正确，是因为对象在调用 `__init__` 时已经被构造出来了，你已经有了一个对类的新实例的有效引用。但 `__init__` 是在 Python 中你可以得到的最接近构造函数的东西，并且它也扮演着非常相似的角色。
| 每个类方法的第一个参数，包括 `__init__`，都是指向类的当前实例的引用。按照习惯这个参数总是被称为 `self`。在 `__init__` 方法中，`self` 指向新创建的对象；在其它的类方法中，它指向方法被调用的类实例。尽管当定义方法时你需要明确指定 `self`，但在调用方法时，你不 用指定它，Python 会替你自动加上的。
| `__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

| 一些伪面向对象语言，像 Powerbuilder 有一种“扩展”构造函数和其它事件的概念，即父类的方法在子类的方法执行前被自动调用。Python 不是这样，你必须显示地调用在父类中的合适方法。
| 我告诉过你，这个类像字典一样工作，那么这里就是第一个印象。我们将参数 `filename` 赋值给对象 `name` 关键字，作为它的值。
| 注意 `__init__` 方法从不返回一个值。

### 5.3.2. 了解何时去使用 `self` 和 `__init__`

当定义你自已的类方法时，你必须 明确将 `self` 作为每个方法的第一个参数列出，包括 `__init__`。当从你的类中调用一个父类的一个方法时，你必须包括 `self` 参数。但当你从类的外部调用你的类方法时，你不必对 `self` 参数指定任何值；你完全将其忽略，而 Python 会自动地替你增加实例的引用。我知道刚开始这有些混乱，它并不是自相矛盾的，因为它依靠于一个你还不了解的区别 (在绑定与非绑定方法之间)，故看上去是矛盾的。

噢。我知道有很多知识需要吸收，但是你要掌握它。所有的 Python 类以相同的方式工作，所以一旦你学会了一个，就是学会了全部。如果你忘了别的任何事，也要记住这件事，因为我认定它会让你出错：

`__init__` 方法是可选的，但是一旦你定义了，就必须记得显示调用父类的 `__init__` 方法 (如果它定义了的话)。这样更是正确的：无论何时子类想扩展父类的行为，后代方法必须在适当的时机，使用适当的参数，显式调用父类方法。

### 进一步阅读关于 Python 类

- Learning to Program 有优雅的类的介绍。
- How to Think Like a Computer Scientist 展示了如何使用类来实现复合数据类型模型。
- Python Tutorial 深入考虑了类、名字空间和继承。
- Python Knowledge Base 回答了关于类的常见问题。

### Footnotes

[2] 在 2.2 之后已经可以从 dict、list 来派生子类了，关于这一点作者在后文也会提到。――译注