目录
- 前言:
- 一:列表和元组
-
- [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。
因为:
alist[0] 是 1
alist[1] 是 2
alist[2] 是 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] 等价于 alist[len(alist)-1]。
这个在实际开发里非常好用。
比如你想拿最后一个元素,C++ 常见写法可能是 vec[vec.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[:]) # 取整个列表 ``` 结果大概是: ![\[2, 3, 4
1, 2, 3
1, 2, 3, 4\]](https://i-blog.csdnimg.cn/direct/23c578e107ee442e9dfd2212443fcbb2.png) 这里的规律其实很简单: **左边不写,默认从头开始 右边不写,默认取到末尾 两边都不写,就是完整复制一份当前列表** 所以你以后看到 \[:\],脑子里就要立刻想到: 这是在"把整个序列切出来"。 ### 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 的细节:切片数字越界一般不会报错,只会尽可能拿到满足条件的元素。 比如课件里有 alist\[100: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) ``` 结果: ![\[1, 2, 3, 4, 'hello'\]](https://i-blog.csdnimg.cn/direct/6548d42e1ca04d47a72fec1c9cdf72a7.png) 如果你不想加到最后,而是想插到中间某个位置,就用 insert:**insert 的第一个参数是插入位置的下标。** ```python alist = [1, 2, 3, 4] alist.insert(1, 'hello') print(alist) ``` 结果: ![\[1, 'hello', 2, 3, 4\]](https://i-blog.csdnimg.cn/direct/8153421d6e34440cb4d656f0da314782.png) 这里你可以这样理解: **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)