python 类和面向对象 之抽象数据类型和类

282 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

类和面向对象

现在,我们将注意力转向与Python编程相关的最后一个主要主题:使用类围绕数据抽象来组织程序。

类可以以许多不同的方式使用。在本书中,我们强调在面向对象编程的上下文中使用它们。面向对象编程的关键是将对象视为数据的集合以及对该数据进行操作的方法。

面向对象编程背后的思想大约有50年的历史,并且在过去的30年左右的时间里被广泛接受和实践。在197os年代中期,人们开始写文章来解释这种编程方法的好处。大约在同一时间,编程语言SmallTalk(在施乐PARC)和CLU(在麻省理工学院)为这些想法提供了语言支持。但直到C++和Java的到来,面向对象编程才真正在实践中起飞。

在本书的大部分时间里,我们一直隐式地依赖于面向对象的编程。回到第2.2.1节,我们说过“对象是Python程序操纵的核心东西。每个对象都有一个类型,该类型定义了程序可以对该对象执行的操作类型。从第 2 章开始,我们依赖于内置类型(如 float 和 str)以及与这些类型关联的方法。但是,正如编程语言的设计者只能构建一小部分有用的函数一样,他们只能构建一小部分有用的类型。我们已经研究了一种机制允许程序员定义新功能;我们现在看一个允许程序员定义新类型的机制。

抽象数据类型和类

抽象数据类型的概念非常简单。抽象数据类型是一组对象以及对这些对象的操作。它们绑定在一起,以便程序员可以将对象从程序的一部分传递到另一部分,并且这样做不仅可以访问对象的数据属性,还可以访问使操作变得容易操作该数据的操作。

这些操作的规范定义了抽象数据类型与程序其余部分之间的接口。该接口定义了操作的行为 - 它们做什么,但不是它们如何做。因此,接口提供了一个抽象屏障,将程序的其余部分与提供类型抽象实现所涉及的数据结构、算法和代码隔离开来。

编程是关于以一种促进变革的方式管理复杂性。两种强大的机制可用于完成这:分解和抽象化。分解在程序中创建结构,抽象抑制细节。关键是要禁止显示适当的细节。这就是数据抽象达到目标的地方。我们可以创建特定于域的类型,以提供方便的抽象。理想情况下,这些类型捕获在程序的整个生命周期中相关的概念。如果我们通过设计几个月甚至几十年后相关的类型来开始编程过程,那么我们在维护该软件方面有很大的优势。

image.png

第一行表示玩具是对象的子类。现在,忽略子类的含义。我们很快就会谈到这一点。类定义创建类型类型的对象,并与该类对象关联一组称为属性的对象。在此示例中,与类关联的三个属性are_init_、添加和大小。每个都是函数类型。因此,

image.png

打印

image.png

正如我们将看到的,Python有许多特殊的函数名称,它们以两个下划线开头和结尾。这些通常被称为魔法方法。我们将要看的第一个是

初始化。每当实例化类时,都会调用

在该类中定义的 init 函数。当代码行

image.png

执行时,解释器将创建一个 Toy 类型的新实例,然后调用Toy._init__with新创建的对象作为绑定到形式参数 self 的实际参数。调用后,Toy._init_creates列表object_elems,该列表将成为新创建的 Toy 类型实例的一部分。(该列表是使用现在熟悉的符号 [] 创建的,它只是一个列表 () 的缩写。list_elems称为 Toy 实例的数据属性。代码

image.png

打印

image.png

请注意,t1.add 是类型方法,而玩具添加是类型函数。因为 t1.add 是一个方法,所以我们可以使用点符号调用它(和 t1.size)。

类不应与该类的实例混淆,就像类型 1ist 的对象不应与列表类型混淆一样。属性可以与类本身或类的实例相关联:

类属性在类定义中定义;例如玩具。大小是类玩具的属性,当类是

实例化,例如,通过语句 t = Toy(),实例属性,例如,t.大小,被创建。

虽然 t. size 最初绑定到类 Toy 中定义的大小函数,但该绑定可以在计算过程中更改。例如,您可以(但绝对不应该!)通过执行 t.size=3 来更改绑定。

当数据属性与类关联时,我们称它们为类变量。当它们与实例关联时,我们称它们为实例变量。例如,_elems是

实例变量,因为对于类 Toy 的每个实例,_elems绑定到不同的列表。到目前为止,我们还没有看到类变量。我们将在 Eigure 10-4 中使用一个。

现在,考虑代码

image.png

由于 Toy 的每个实例都是不同的对象,因此 Toy 类型的每个实例都将具有different_elems属性。因此,代码打印 3。

乍一看,此代码中似乎有不一致的地方。看起来每个方法都使用一个参数调用得太少。例如,add 有两个正式参数,但我们似乎只用一个实际参数来调用它。这是使用点表示法调用与类的实例关联的方法的工件。与点前面的表达式关联的对象将作为第一个参数隐式传递给方法。在本书中,我们遵循使用 self 作为这个实际参数绑定到的形式参数的名称的惯例。Python程序员几乎普遍遵守这个约定,我们强烈建议你也使用它。

另一个常见的约定是以下划线开头的数据属性名称。正如我们在第 10.3 节中详细讨论的那样,我们使用前导_to指示该属性对于类是私有的,即不应在类外部直接访问。

现在,让我们看一个更有趣的例子。Eigure 10-1 包含一个类定义,该定义提供了一组称为Int_set的整数抽象的直接实现。(鉴于Python有一个内置的类型集,这个实现既不必要又不必要地复杂。但是,它在教学上是有用的。