5.3. 定义类

Python 是完全面向对象的:您可以定义自己的类,从您自己的类或内置类继承,并实例化您定义的类。

Python 中定义一个类很简单。与函数一样,没有单独的接口定义。只需定义类并开始编码。一个 Python 类以保留字 class 开始,后面跟着类名。从技术上讲,这就是必需的全部内容,因为类不需要从任何其他类继承。

示例 5.3. 最简单的 Python


class Loaf: 1
    pass    2 3
1 这个类的名称是 Loaf,它没有从任何其他类继承。类名通常是大写的,例如 每个单词都像这样,但这只是一个约定,而不是必需的。
2 这个类没有定义任何方法或属性,但是从语法上讲,定义中需要有一些东西,所以您使用 pass。这是一个 Python 保留字,意思是“继续前进,这里没什么可看的”。它是一个什么也不做的语句,当您在填充函数或类时,它是一个很好的占位符。
3 您可能已经猜到了,类中的所有内容都是缩进的,就像函数、if 语句、for 循环等中的代码一样。第一个没有缩进的内容不在类中。
Note
Python 中的 pass 语句类似于 JavaC 中的一组空括号 ({})。

当然,实际上,大多数类都将从其他类继承,并且它们将定义自己的类方法和属性。但是正如您刚刚看到的,除了名称之外,类没有绝对必须拥有的东西。特别是,C++ 程序员可能会发现 Python 类没有显式的构造函数和析构函数很奇怪。Python 类确实有类似于构造函数的东西:__init__ 方法。

示例 5.4. 定义 FileInfo


from UserDict import UserDict

class FileInfo(UserDict): 1
1 Python 中,类的祖先简单地列在类名后面的括号中。所以 FileInfo 类是从 UserDict 类继承的(它是从 UserDict 模块导入的)。UserDict 是一个像字典一样工作的类,允许您基本上子类化字典数据类型并添加您自己的行为。(还有类似的类 UserListUserString 允许您子类化列表和字符串。)这背后有一些黑魔法,当您在本章后面更深入地探讨 UserDict 类时,您将揭开它的神秘面纱。
Note
Python 中,类的祖先简单地列在类名后面的括号中。没有像 Java 中的 extends 这样的特殊关键字。

Python 支持多重继承。在类名后面的括号中,您可以列出任意数量的祖先类,用逗号分隔。

5.3.1. 初始化和编写类

此示例显示了使用 __init__ 方法初始化 FileInfo 类。

示例 5.5. 初始化 FileInfo


class FileInfo(UserDict):
    "store file metadata"              1
    def __init__(self, filename=None): 2 3 4
1 类也可以(并且应该)像模块和函数一样拥有 文档字符串
2 __init__ 在创建类的实例后立即被调用。将此称为类的构造函数是诱人的,但不正确。之所以诱人,是因为它看起来像一个构造函数(按照惯例,__init__ 是为类定义的第一个方法),行为也像一个构造函数(它是新创建的类实例中执行的第一段代码),甚至听起来也像一个构造函数(“init”当然暗示了构造函数的性质)。不正确,因为在调用 __init__ 时,对象已经被构造出来了,并且您已经拥有对新类实例的有效引用。但是 __init__ 是您在 Python 中最接近构造函数的东西,它起着几乎相同的作用。
3 每个类方法(包括 __init__)的第一个参数始终是对当前类实例的引用。按照惯例,此参数始终命名为 self。在 __init__ 方法中,self 指的是新创建的对象;在其他类方法中,它指的是调用其方法的实例。虽然您在定义方法时需要显式指定 self,但在调用方法时需要指定它;Python 会自动为您添加它。
4 __init__ 方法可以接受任意数量的参数,就像函数一样,参数可以用默认值定义,使其对调用者可选。在这种情况下,filename 的默认值为 None,它是 Python 的空值。
Note
按照惯例,任何 Python 类方法的第一个参数(对当前实例的引用)都被称为 self。此参数在 C++Java 中扮演着保留字 this 的角色,但 selfPython 中不是保留字,而只是一个命名约定。尽管如此,请不要将它称为 self 以外的任何东西;这是一个非常强的约定。

示例 5.6. 编写 FileInfo


class FileInfo(UserDict):
    "store file metadata"
    def __init__(self, filename=None):
        UserDict.__init__(self)        1
        self["name"] = filename        2
                                       3
1 一些伪面向对象语言(如 Powerbuilder)具有“扩展”构造函数和其他事件的概念,其中在执行后代方法之前会自动调用祖先方法。Python 不会这样做;您必须始终在祖先类中显式调用适当的方法。
2 我告诉过您,这个类就像一个字典,这是它的第一个迹象。您正在将参数 filename 分配为此对象的 name 键的值。
3 请注意,__init__ 方法从不返回值。

5.3.2. 何时使用 self__init__

在定义类方法时,您必须显式列出 self 作为每个方法的第一个参数,包括 __init__。当您从类内部调用祖先类的方法时,您必须包含 self 参数。但是当您从外部调用类方法时,您不需要为 self 参数指定任何内容;您完全跳过它,Python 会自动为您添加实例引用。我知道这乍一看令人困惑;它实际上并不矛盾,但它可能看起来矛盾,因为它依赖于您尚不了解的区别(绑定方法和未绑定方法之间)。

呼。我知道这需要吸收很多东西,但你会掌握它的。所有 Python 类的工作方式都相同,所以一旦你学会了一个,你就学会了所有。如果你忘记了其他一切,请记住这一件事,因为我保证它会让你绊倒

Note
__init__ 方法是可选的,但当您定义一个方法时,您必须记住显式调用祖先的 __init__ 方法(如果它定义了一个)。更普遍地说:每当后代想要扩展祖先的行为时,后代方法必须在适当的时候用适当的参数显式调用祖先方法。

关于 Python 类的拓展阅读