学径 LearnPath

编程语言基础

类与对象

封装同类事物的属性与行为。

讲解模式

通过本节课,你将理解:

#goal

上一课把「解方程」封进 函数:传入系数,返回未知数。函数擅长做完一件事就结束。

可是同一组系数往往还要被打印、被检查、被拿去和另一条方程比较。系数散落在四个参数里,没有一个名字把「这条方程」收成事物。

类 描述一类事物:它有哪些数据、能做哪一种计算。按这张描述造出来的具体存在,叫 对象。类是图纸,对象是按图纸存在的那一件。

本节课你将学会用 class 与 __init__ 保存状态,并用方法在该状态上工作。

运行环境仍是本平台的 在线 IDE。

一份结构,既保存信息,也规定行为

#hook

DNA 双螺旋常被说成「生命的说明书」。更精确的说法是:它是一种把信息编码进结构的方式;细胞读这份结构,才表现出复制、转录那些行为。

结构与行为不是两套互不相干的清单。本课借用的是这层关系——并不宣称「类就是 DNA」。

DNA 双螺旋结构示意图
DNA 双螺旋结构示意图。Zephyris。来源:Wikimedia Commons,CC BY-SA 3.0。

对象要模仿的,正是这种「事物」:一条方程拥有自己的系数,并且知道如何求解。图纸说明一类东西长什么样、会做什么;实例按图纸存在,并各自带上自己的那一份信息。

只有函数时,相关的数据会散开

#explore-scatter

solve(2, 3, 0, 7) 能交出一个解。下一次若要打印这四个系数,必须把这四个数再写一遍;再比较,又是四个参数。

数据没有住处,只能在每一次调用的参数表里重新集合。人会数错、抄漏;概念上也没有「这一条方程」——存在的只有若干次对 solve 的访问。

把四个系数收进一个对象,不是为了少写几个数字。意义在于:写过一次之后,这四个数有了一个共同的主人。之后的求解、打印、比较,都向这个主人提问。

类与构造:先造出这一件

#explore-class

class 后面是类的名字,习惯用大写开头。__init__ 在造对象时自动执行,把传入的数据贴到对象身上:

python
class LinearEquation:
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d


eq = LinearEquation(2, 3, 0, 7)
print(eq.a, eq.b, eq.c, eq.d)

self 表示正在构造的那一个实例——同一张图纸会造出许多件,方法必须知道此刻操作的是哪一件。

另一个对象 other = LinearEquation(-5, 10, 0, 0) 有自己的系数,互不覆盖。图纸只有一份,成品可以有许多份——这正是类与对象的差别。

方法:对象能做的那件事

#explore-method

把求解写进方法。系数已经住在对象里,调用时不必再把四个数递过去:

python
class LinearEquation:
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

    def solve(self):
        A = self.a - self.c
        D = self.d - self.b
        return D / A


eq = LinearEquation(2, 3, 0, 7)
print(eq.solve())

2x+3=72x + 3 = 7,解为 2.0。点号左边是对象,右边是方法名;self 不必传入,Python 会把点号左边的对象自动填进第一个参数。

属性必须通过 self 点出来。写成 A = a - c 而不是 A = self.a - self.c,机器会到别处寻找名叫 a 的变量。

先问有没有「那一件」

#explore-when

两条方程共用 solve 的代码,却各自使用自己的属性:

python
eq1 = LinearEquation(2, 3, 0, 7)
eq2 = LinearEquation(-5, 10, 0, 0)
print(eq1.a, eq2.a)
print(eq1.solve(), eq2.solve())

eq1.a 仍是 2。后造的对象不会覆盖先造的属性。

并非每个计算都需要对象。一次性的换算、没有必要反复提起的中间量,函数足够。只有当「这一件」会在多个步骤里被提起,准备一张图纸才换来清晰。

函数抽象过程;对象抽象带状态的事物。没有「这一件」,相关数据就会在参数表里反复散开。

本节练习:两段区间

#practice

在 在线 IDE 里定义类 Interval:构造时保存左端 left 与右端 right,方法 length 用 return 交回右端减左端。先造 (0, 5),再造 (-2, 2)。打印两者的长度(应为 5 与 4),并确认第一条区间的左端仍是 0。类是图纸,两条区间是各自带着端点的对象。

本节要点

#review
  • 类是一类事物的描述,对象是按这张描述存在的实例。图纸说明结构与行为;实例拥有自己的状态。
  • __init__ 把数据写成属性;self 指向当前这个对象。
  • 方法使用该实例自己的状态。函数抽象过程,对象抽象带状态的事物。
  • 没有需要反复提起的「那一件」时,普通函数通常已经足够。

单选题

图纸还是成品

关于 class LinearEquation 和 eq = LinearEquation(2, 3, 0, 7),哪一句正确?

单选题

self 的作用

调用 eq.solve() 时,方法定义里的 self 指的是?

单选题

属性存在哪里

eq1 = LinearEquation(2, 3, 0, 7),eq2 = LinearEquation(-5, 10, 0, 0)。此时 eq1.a 是?

单选题

为何不只用函数

已经有 solve(a, b, c, d) 能解方程,仍把系数收进对象,主要是为了什么?

单选题

何时不必用类

下面哪一种情况,用普通函数通常已经足够?

未登录时不会保存学习进度