目录
- 前言:
- 一:列表和元组
-
- [1. 列表到底是什么](#1. 列表到底是什么)
- [2. 如何创建列表](#2. 如何创建列表)
- 3.下标访问:列表不是一坨,它是有位置的
- [4. 下标越界:不是所有位置都能访问](#4. 下标越界:不是所有位置都能访问)
- [5. 负数下标:从后往前数](#5. 负数下标:从后往前数)
- [6. 切片:一次不是取一个,而是取一段](#6. 切片:一次不是取一个,而是取一段)
- [7. 切片省略边界:从头切、切到尾、全切出来](#7. 切片省略边界:从头切、切到尾、全切出来)
- [8. 切片步长:不是一个一个取,而是隔着取](#8. 切片步长:不是一个一个取,而是隔着取)
- [9. 遍历列表:把元素一个一个拿出来处理](#9. 遍历列表:把元素一个一个拿出来处理)
- [10. 新增元素:append 和 insert](#10. 新增元素:append 和 insert)
- [11. 查找元素:in 和 index](#11. 查找元素:in 和 index)
- [12. 删除元素:pop 和 remove](#12. 删除元素:pop 和 remove)
- [13. 连接列表:+ 和 extend](#13. 连接列表:+ 和 extend)
- [14. 元组:和列表很像,但它"不能改"](#14. 元组:和列表很像,但它“不能改”)
- [15. 为什么明明有列表了,还要有元组](#15. 为什么明明有列表了,还要有元组)
- [16. 函数返回多个值时,背后其实就是元组](#16. 函数返回多个值时,背后其实就是元组)
- [17. 列表和元组到底怎么选](#17. 列表和元组到底怎么选)
前言:
前面学函数时,我们处理的大多是"一个变量对应一个值"。
但是实际写程序时,经常会遇到这种场景:
一个班几十个学生成绩
一堆商品价格
一串坐标点
一批文件名
这时候如果还写成:
python
num1 = 10
num2 = 20
num3 = 30
...
就会非常笨重,
当数据个数很多,甚至个数都不确定时,就需要用列表来批量保存数据。 元组和列表很像,但元组一旦创建后内容不能改,而列表可以改。
就比如:
列表像袋子,随时能往里放、往外拿;元组像已经封装好的包装,里面多少就是多少
一:列表和元组
1. 列表到底是什么
你可以先把列表理解成:
Python 里用来按顺序存一批数据的容器。
这个理解非常重要。
它不是一个单独的数据,而是一串有顺序的数据。
比如:
python
scores = [95, 88, 76, 100]
这个 scores 不是一个数字,而是一整个"序列"。
类比C++,可以把它先粗略类比成 vector。但要注意,Python 的列表比 C++ 的 vector 更灵活,列表里允许存放不同类型的元素。 也就是说,你甚至可以这么写:
python
alist = [1, 'hello', True]
这在 Python 里是合法的。
但在 C++ 里,vector 通常要求元素类型一致,所以这也是 Python 动态类型很典型的体现。
2. 如何创建列表
两种最基础的创建方式:
python
alist = []
alist = list()
这两种都表示创建一个空列表。
如果你想创建时直接放入初始值,就写到方括号里:
python
alist = [1, 2, 3, 4]
print(alist)
注意:不要拿 list 当变量名,因为 list 本身是 Python 的内建函数
这这就像在 C++ 里最好别拿 string、vector 这些名字去乱当变量名,不然可读性会变差,甚至引发冲突。
3.下标访问:列表不是一坨,它是有位置的
列表里的元素是有顺序的,所以每个元素都有自己的位置。
这里把这个位置叫做 下标 或 索引 。
注意:Python 列表下标从 0 开始 。
比如:
python
alist = [1, 2, 3, 4]
print(alist[2])

这里输出的不是 2,而是 3。
因为:
alist0 是 1
alist1 是 2
alist2 是 3
这和 C++ 数组、vector 的下标规则是一致的。
更重要的是:下标不只是能读,还能改。 比如:
python
alist = [1, 2, 3, 4]
alist[2] = 100
print(alist)
执行后列表变成:
python
[1, 2, 100, 4]
这说明列表是可变对象,这也是它和元组最大的区别之一。
4. 下标越界:不是所有位置都能访问
一个常见错误:如果下标超出列表有效范围,会抛异常。
比如:
python
alist = [1, 2, 3, 4]
print(alist[100])

这会报 IndexError: list index out of range 。
原因很简单:列表里根本没有第 101 个元素。
说明:
如果列表长度是 len(alist),那么有效下标范围就是 0, len(alist)-1。
例如:
python
alist = [1, 2, 3, 4]
print(len(alist)) # 4
那它的合法下标就是:
python
0 1 2 3
5. 负数下标:从后往前数
这个是 Python 很方便的地方=:下标可以取负数,表示倒数第几个元素。
例如:
python
alist = [1, 2, 3, 4]
print(alist[-1])

因为:
-1 表示倒数第一个
-2 表示倒数第二个
-3 表示倒数第三个
对应关系:
alist-1 等价于 alistlen(alist)-1。
这个在实际开发里非常好用。
比如你想拿最后一个元素,C++ 常见写法可能是 vecvec.size() - 1,而 Python 直接 alist-1,又短又直观。
6. 切片:一次不是取一个,而是取一段
前面的下标访问,一次拿一个元素。 切片,它的本质是:
一次取出一组连续元素,得到一个子列表。
最基础写法:
python
alist = [1, 2, 3, 4]
print(alist[1:3])
结果是:

这里特别容易踩坑,所以必须讲清楚:
1:3 表示的是 前闭后开区间 [1, 3)。
也就是:
包含下标 1
包含下标 2
不包含下标 3
所以最后得到 2 和 3,而不是 2、3、4。
并且:+ Python 里很多地方都在用"前闭后开"这个思想。
7. 切片省略边界:从头切、切到尾、全切出来
课件继续讲了切片边界可以省略。
比如:
python
alist = [1, 2, 3, 4]
print(alist[1:]) # 从下标1取到末尾
print(alist[:-1]) # 从开头取到倒数第一个之前
print(alist[:]) # 取整个列表
结果大概是:

这里的规律其实很简单:
左边不写,默认从头开始
右边不写,默认取到末尾
两边都不写,就是完整复制一份当前列表
所以你以后看到 :,脑子里就要立刻想到:
这是在"把整个序列切出来"。
8. 切片步长:不是一个一个取,而是隔着取
切片还能写第三个参数,表示 步长。
例如:
python
alist = [1,2,3,4,5,6,7,8,9,10]
print(alist[::1])
print(alist[::2])
print(alist[::3])
大概可以理解为:
::1 表示一个一个取
::2 表示隔一个取一个
::3 表示每隔两个取一个
这就像你走楼梯:
步长 1:一级一级走
步长 2:两级两级跳
步长 3:三级三级跳
并且:步长可以是负数,这就表示从后往前取。
例如:
python
alist = [1,2,3,4,5,6,7,8,9,10]
print(alist[::-1])
这就是反转列表,结果是:
python
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
这个写法很经典,以后你会经常看到。
还有一个很 Python 的细节:切片数字越界一般不会报错,只会尽可能拿到满足条件的元素。 比如课件里有 alist100:200,结果不是异常,而是空列表 \[\]。
所以要记住:
单个下标越界:报错
切片越界:通常不报错,尽量给结果
9. 遍历列表:把元素一个一个拿出来处理
把元素一个一个取出来,再分别处理。
最常见的遍历方式是 for:
python
alist = [1, 2, 3, 4]
for elem in alist:
print(elem)
这个是最推荐的基础写法,因为简单直接。
但是Python 还给了两种变体。
第一种,用下标遍历:
python
alist = [1, 2, 3, 4]
for i in range(0, len(alist)):
print(alist[i])
第二种,用 while 手动控制下标:
python
alist = [1, 2, 3, 4]
i = 0
while i < len(alist):
print(alist[i])
i += 1
这三种都能遍历,但在日常开发中:
只关心元素本身,用 for elem in alist
既想要下标又想要元素,再考虑别的写法
10. 新增元素:append 和 insert
append:它表示 尾插,也就是把新元素加到列表最后面。
python
alist = [1, 2, 3, 4]
alist.append('hello')
print(alist)
结果:

如果你不想加到最后,而是想插到中间某个位置,就用 insert:insert 的第一个参数是插入位置的下标。
python
alist = [1, 2, 3, 4]
alist.insert(1, 'hello')
print(alist)
结果:

这里你可以这样理解:
append:直接加尾巴
insert:指定位置塞进去
11. 查找元素:in 和 index
第一个是 in,判断某个元素在不在列表里,返回布尔值:
python
alist = [1, 2, 3, 4]
print(2 in alist)
print(10 in alist)
结果大概是:
python
True
False
第二个是 index,找某个元素第一次出现的位置:
python
alist = [1, 2, 3, 4]
print(alist.index(2))
结果是:
python
1
因为 2 在下标 1 的位置。
但你一定要小心一个坑:如果元素不存在,index 会抛异常。
python
alist = [1, 2, 3, 4]
print(alist.index(10)) # 报错

所以实际写代码时,通常更稳一点的思路是:
先用 in 判断
再决定要不要 index
这就是"先试探,再深入"。
12. 删除元素:pop 和 remove
pop():按位置删
python
alist = [1, 2, 3, 4]
alist.pop()
print(alist)
结果:
python
[1, 2, 3]
这表示删除最后一个元素。
注意:pop 也可以带下标,表示删指定位置的元素。
python
alist = [1, 2, 3, 4]
alist.pop(2)
print(alist)
结果:
python
[1, 2, 4]
remove():按值删
python
alist = [1, 2, 3, 4]
alist.remove(2)
print(alist)
结果:
python
[1, 3, 4]
这次删掉的是"值为 2 的元素",不是"下标为 2 的元素"。
所以这里特别容易混:
pop(2):删的是第 3 个位置上的元素
remove(2):删的是值等于 2 的元素
13. 连接列表:+ 和 extend
两个"拼接列表"的方式:
先看 +:
alist = 1, 2, 3, 4
blist = 5, 6, 7
print(alist + blist)
结果:

但要注意 :
+ 会生成一个新的列表,不会修改旧列表。
也就是说,alist 自己没变,blist 也没变,只是临时拼出来一个新结果。
再看 extend:
python
alist = [1, 2, 3, 4]
blist = [5, 6, 7]
alist.extend(blist)
print(alist)
print(blist)
执行后:

alist 会变成 1,2,3,4,5,6,7
blist 还是 5,6,7
a.extend(b) 是把 b 的内容接到 a 的末尾,不改 b,但会改 a。
所以这里的区别就是:
+:生成新列表
extend:原地扩展原列表
14. 元组:和列表很像,但它"不能改"
最核心的一句话就是:元组和列表功能基本一致,但元组里的元素不能修改。
元组用小括号表示:
python
atuple = ()
atuple = tuple()
它和列表的关系,你可以这样理解:
列表:可变序列
元组:不可变序列
像这些"读操作"元组也支持:
下标访问
切片
遍历
in
index
+
但是这些"写操作"元组不支持:
修改元素
新增元素
删除元素
extend
所以元组不是"功能差的列表",而是"更稳定、更安全的列表"。
15. 为什么明明有列表了,还要有元组
1.第一层原因:安全
如果你有一组数据,不希望它被函数随手改乱,那么传元组会更稳。
因为列表可变,函数拿到以后可能改内容;元组不可变,别人想改也改不了。意思就是:当你不确定某个函数会不会把数据弄乱时,传元组更安全。
2.第二层原因:元组可以当字典的键
字典的键要求是 可 hash 的对象,而"可 hash"的一个重要前提就是"不可变"。于是:
元组可以作为字典的键
列表不行
这也是元组存在的重要工程意义。
16. 函数返回多个值时,背后其实就是元组
这个点和你前面学函数正好连起来。
当一个函数返回多个值时,很多时候默认得到的其实是元组。
例如:
python
def getPoint():
return 10, 20
result = getPoint()
print(type(result))
输出类型其实是:
python
<class 'tuple'>
所以你之前看到这种写法:
python
x, y = getPoint()
本质上就是:
getPoint() 返回了一个元组 (10, 20)
17. 列表和元组到底怎么选
你可以把它总结成一句话:
需要改,用列表;不需要改,用元组。
再展开一点就是:
只要你表示的是一串有顺序的数据,都可以考虑列表/元组
如果后续要增删改,优先列表
如果数据是固定的、不想被改,优先元组
例如:
适合列表:学生成绩后续还会录入、修改
python
scores = [90, 88, 76]
适合元组:一个点的坐标通常就是固定的
python
point = (10, 20)