把「数据」和「数据的用法」绑在一起。前几章用函数已经能写程序了,这一章给你一个更顺手的组织方式。
同一个需求——给一个学生算等级、做名片——两种写法都能写。你更习惯哪种,就从哪种读起。
一个学生,就是一个字典;处理学生,就是一些函数:
设 小明 = {"名字": "小明", "分数": 92}
函数 等级(一名学生)
如果 一名学生["分数"] >= 90
返回 "优秀"
否则 如果 一名学生["分数"] >= 60
返回 "及格"
否则
返回 "不及格"
函数 名片(一名学生)
返回 @"{一名学生['名字']}:{一名学生['分数']} 分({等级(一名学生)})"
打印(名片(小明)) # 小明:92 分(优秀)
这套写法你已经会了——字典在字典学过,函数在函数入门学过。数据和操作是分开的:数据装在字典里,操作是外面的函数,用的时候把数据传进去(等级(小明))。
类 学生
初始化(名字, 分数)
函数 等级()
如果 自己.分数 >= 90
返回 "优秀"
否则 如果 自己.分数 >= 60
返回 "及格"
否则
返回 "不及格"
函数 名片()
返回 @"{自己.名字}:{自己.分数} 分({自己.等级()})"
设 小明 = 学生("小明", 92)
打印(小明.名片()) # 小明:92 分(优秀)
结果一模一样,读起来不同:
| 写法一(字典 + 函数) | 写法二(类) | |
|---|---|---|
| 数据放哪 | 字典里 | 对象的「字段」里 |
| 操作放哪 | 外面的函数 | 类里面的「方法」 |
| 怎么用 | 等级(小明),把数据传进去 |
小明.等级(),对象自己做事 |
| 像什么 | 工具箱和零件分开放 | 零件自带说明书 |
写法二的好处:小明.等级() 读起来像「小明,告诉我你的等级」——数据和它的用法长在一起。数据多了之后,这种「长在一起」会让代码更好读、更好改。
没写过面向对象(类、对象、实例这些词)没关系,这一节从零讲。
写过(比如 Python 的 class),重点看和 Python 不一样的三个地方:初始化参数自动成字段、方法不写 self、没有继承。
类是「模板」,对象(也叫实例)是用模板造出来的具体东西。
拿「学生」打比方:
学生("小明", 92) 是用模板造出来的一个具体学生——这是对象。学生("小红", 85) 会用同一个模板造出另一个对象。两个对象各自独立,数据互不干扰。
类 学生
初始化(名字, 分数)
函数 等级()
如果 自己.分数 >= 90
返回 "优秀"
返回 "继续加油"
一个类由两部分组成:
初始化(...):造对象时怎么填数据。括号里的参数会自动变成字段(见下一节)。函数 ...():这个类有哪些方法(它会的「技能」)。类、初始化 是关键字。方法写法和普通函数一样,只是写在类里面、缩进一层。
初始化 括号里写几个参数,对象就有几个字段,不用手动转存:
类 学生
初始化(名字, 分数)
# 到这里,「名字」「分数」已经是字段了,不用写 自己.名字 = 名字
设 小明 = 学生("小明", 92)
打印(小明.名字) # 小明
打印(小明.分数) # 92
对比 Python:Python 要写 self.name = name 一行行转存;CNplus 直接省略——参数就是字段。
初始化 的块体可以省略(只造字段就够时)。要写校验或派生字段时才需要块体:
类 学生
初始化(名字, 分数)
如果 分数 < 0 或 分数 > 100
抛出 @"分数必须在 0 到 100 之间,{名字}的分数是 {分数}"
函数 报分()
打印(自己.分数)
分数不合法时,造对象当场报错(抛出 见异常处理)。
初始化里写返回也能用(提前结束),但返回值会被忽略——造出来的始终是新对象。
方法里用 自己 指代当前这个对象,然后 .字段 取它的字段:
类 学生
初始化(名字, 分数)
函数 名片()
返回 @"{自己.名字}:{自己.分数} 分"
设 小明 = 学生("小明", 92)
设 小红 = 学生("小红", 85)
打印(小明.名片()) # 小明:92 分
打印(小红.名片()) # 小红:85 分
小明.名片() 里,自己 就是小明;小红.名片() 里,自己 就是小红。同一个方法,两个对象调用,结果各用各的数据。
两个省心的地方:
def 名片(self),CNplus 不写,自己 是隐式关键字。自己 只在方法里有效:方法外写 自己.xxx 会报「未声明」(CN0302)。自己.:直接写裸字段名(名字 而非 自己.名字)会报 CN0302——成员要显式经由 自己 访问。学过 Python 的人看,零基础可跳过。
先看一个完整的 CNplus 程序:
类 计数器
初始化(起点)
函数 加一()
自己.起点 = 自己.起点 + 1
返回 自己.起点
设 一个 = 计数器(10)
打印(一个.加一())
同一个程序,用 Python 写:
class 计数器:
def __init__(self, 起点):
self.起点 = 起点
def 加一(self):
self.起点 += 1
return self.起点
一个 = 计数器(10)
print(一个.加一())
| CNplus | Python |
|---|---|
类 计数器 |
class 计数器: |
初始化(起点) 自动建立字段 |
__init__ 中手动写 self.起点 = 起点 |
自己.起点 |
self.起点 |
方法参数不用写 自己 |
每个实例方法都要显式写 self |