入门会建列表、取元素了。这一节学切片、以及改列表的各种函数。
用 [起:止] 从列表里切一段,含起点、不含终点(左闭右开):
设 数们 = [1, 2, 3, 4, 5]
打印(数们[1:3]) # [2, 3](第 1、2 号,不含第 3 号)
打印(数们[2:]) # [3, 4, 5](从第 2 号到结尾)
打印(数们[:2]) # [1, 2](从头到第 2 号前)
打印(数们[:]) # [1, 2, 3, 4, 5](整个,等于复制一份)
[2:] 到结尾、[:2] 从头、[:] 整个。数们[-2:] 是最后两个、数们[:-1] 是去掉最后一个。数们[1:99] 等于 数们[1:]——切一段,段短了没关系。这跟单个索引越界报错是刻意不一样:单索引是「取一个具体位置」,位置不存在就该报错;切片是「取一段」,能取多少取多少。数们[2:1] 得 []。在后面再加一段,就能隔着取:
列表[起点:终点:步长]
先看这排名字和位置:
位置 0 1 2 3 4 5 6
名字 甲 乙 丙 丁 戊 己 庚
↑ ↑ ↑
1 →+2 3 →+2 5
从位置 1 出发,每次移动 2 个位置,会依次停在 1、3、5:
设 名单 = ["甲", "乙", "丙", "丁", "戊", "己", "庚"]
打印(名单[1:6:2]) # [乙, 丁, 己]
所以,步长 2 表示相邻两次取值的位置相差 2,中间隔过一项,不是“跳过两项”。
一句话记住:步长决定往哪边走、每次走几格,不决定一共取几个。 正数向右走,负数向左走。
起点、终点和步长都能省略,按这个顺序慢慢看:
设 名单 = ["甲", "乙", "丙", "丁", "戊", "己", "庚"]
打印(名单[1:6:2]) # [乙, 丁, 己](三段都写)
打印(名单[:6:2]) # [甲, 丙, 戊](从开头开始)
打印(名单[1::2]) # [乙, 丁, 己](走到末尾之后)
打印(名单[::2]) # [甲, 丙, 戊, 庚](从头隔一项取一个)
打印(名单[::-1]) # [庚, 己, 戊, 丁, 丙, 乙, 甲](从末尾倒着走)
不写步长就等于写 1,所以原来的 [起:止] 仍然是最常用的写法。
⚠️ 两个看起来很像的写法:
名单[::-1]省略了终点,负步长会一直走到“开头之前”,所以能完整倒序;名单[:-1:-1]的-1是明确写出的末项位置,起点和终点落在同一处,因此结果是[]。第一次看到觉得绕,可以先记结论:倒序用[::-1],不要补-1。
名单[-99:99:2] 仍能正常取值。[][::2] 得 []。真、假 是布尔,不是位置编号,不能拿来给列表或文字取值。小数 1.0 看起来像整数,但它仍是小数;这些情况都报 CN0303。步长还不能是 0。名单[::2] = ... 来改原列表。例如,下面不是“第 1 个”,而是把布尔值当位置,会报 CN0303:
打印([10, 20][真])
字典不按位置找值,
真可以作为字典标签,而且和整数标签1不同。详见与 Python 的差异。
文字也能切片,而且 emoji 算一个完整的字,不会被拆成半个字符:
设 文字 = "甲😀乙丙"
打印(文字[::2]) # 甲乙
打印(文字[::-1]) # 丙乙😀甲
切片是复制出一段新的,改切片不影响原来的列表:
设 数们 = [1, 2, 3]
设 前两个 = 数们[:2]
追加(前两个, 99)
打印(数们) # [1, 2, 3](没变)
打印(前两个) # [1, 2, 99]
| 函数 | 作用 | 例子 |
|---|---|---|
追加(表, 项) |
末尾加一个 | 追加(同学们, "小美") |
插入(表, 位, 项) |
插到指定位置 | 插入(同学们, 1, "小丽") |
移除(表, 项) |
删掉某个值 | 移除(同学们, "小红") |
弹出(表) |
拿走最后一个 | 设 最后 = 弹出(同学们) |
弹出(表, 位) |
拿走指定位置 | 弹出(同学们, 0) |
设 名单 = ["甲", "乙", "丙"]
追加(名单, "丁")
打印(名单) # [甲, 乙, 丙, 丁]
插入(名单, 1, "插队")
打印(名单) # [甲, 插队, 乙, 丙, 丁]
设 拿走的 = 弹出(名单)
打印(拿走的) # 丁
打印(名单) # [甲, 插队, 乙, 丙]
追加、插入、移除、弹出会直接改原列表;而排序、倒序是返回一个新列表,原列表不动。
这些函数也能「挂」在列表上点出来,效果完全一样,读起来更像一句话:
设 名单 = ["甲", "乙"]
名单.追加("丙") # 等价于 追加(名单, "丙")
打印(名单) # [甲, 乙, 丙]
打印(名单.长度()) # 等价于 长度(名单) → 3
也可以这么写:
名单.追加(x)和追加(名单, x)是同一个函数的两种写法,字典、字符串上支持点调用的内置函数也都能点调用。这是 CNplus 的特性(成员方法),详见关键字与别名。
设 数们 = [3, 1, 2]
打印(排序(数们)) # [1, 2, 3] (返回新列表)
打印(数们) # [3, 1, 2] (原列表没变)
打印(倒序(数们)) # [2, 1, 3]
打印(包含(数们, 2)) # 真
打印(包含(数们, 9)) # 假
打印(连接(数们, "-")) # "3-1-2"(连成一条文字)
文字也可以用 倒序,结果仍是文字。完整倒序时,下面三种写法得到相同结果:
设 原文字 = "甲😀乙"
打印(倒序(原文字)) # 乙😀甲
打印(原文字.倒序()) # 乙😀甲
打印(原文字[::-1]) # 乙😀甲
打印(原文字) # 甲😀乙(原文字没变)
怎么选,看你正在做什么:
倒序(原文字):先说操作,像“倒序这段文字”;原文字.倒序():先说主语,和其他点调用写法一致;原文字[::-1]:已经在用切片选择范围、方向或间隔时最顺手。这里只能说它们在“完整倒序”时结果相同,不代表内部是同一种实现。三种写法都会产生新值,不修改原来的列表或文字。
设 同学们 = ["小明", "小红", "小刚"]
打印(长度(同学们)) # 3
访问不存在的编号,报 CN0308:
设 同学们 = ["小明"]
打印(同学们[5])
# 错误 CN0308:下标 5 超出范围(一共 1 个)
学过 Python 的人,看这里——这一节学的,用 Python 写是这样。零基础可以直接跳过,去下面做「练习二」。
先把这一节串成一个完整程序(CNplus):
设 数们 = [1, 2, 3, 4]
追加(数们, 5)
打印(数们[0]) # 第一个
打印(数们[-1]) # 最后一个
打印(数们[1:3]) # 切一段 → [2, 3]
打印(数们[::2]) # 隔一项取一个 → [1, 3, 5]
打印(数们[::-1]) # 倒着取 → [5, 4, 3, 2, 1]
同一个程序,用 Python 写:
数们 = [1, 2, 3, 4]
数们.append(5) # 追加(数们, 5)
print(数们[0]) # 第一个
print(数们[-1]) # 最后一个
print(数们[1:3]) # 切一段 → [2, 3]
print(数们[::2]) # 隔一项取一个 → [1, 3, 5]
print(数们[::-1]) # 倒着取 → [5, 4, 3, 2, 1]
| CNplus | Python |
|---|---|
追加(表, 项) |
表.append(项) |
插入(表, 位, 项) |
表.insert(位, 项) |
移除(表, 项) |
表.remove(项) |
弹出(表) |
表.pop() |
长度(表) |
len(表) |
包含(表, 项) |
项 in 表 |
表.追加(x) |
表.append(x)(点调用就是 Python 风格) |
表[起:止:步长] |
表[起:止:步长](表面写法相同) |
切片、负索引两边的表面写法几乎一模一样,但 CNplus 的省略边界、Unicode 文字、类型限制和求值顺序由 CNplus 自己的规范决定,Python 或 JavaScript 只是执行后端,不是规则的裁判。列表函数 CNplus 有两种写法(追加(表, 项) 或 表.追加(项)),Python 只有点调用一种。
下一步:做练习二。