第12章-列表


先想一个问题:你去超市买东西,是不是要拿一张购物单,一样一样往上记?程序里也有同样的需求——存一串数据:一个班的成绩、一购物车的商品、一页通讯录。存这一串东西的容器,就是今天的主角:列表

昨天的字符串也是一串东西,但它是"文字",只能读不能改;列表是"盒子",能增、能删、能改。购物车、成绩单、待办清单……凡是"一串数据",都得用列表装。所以列表是本周的必会项,后面所有项目——不管是写通讯录还是做记账本——数据都要先装进列表,再靠循环逐个处理。今天学的增删改查,就是操作列表的四个基本动作。

列表长什么样:方括号装一串东西

列表用方括号 [] 包起来,里面的东西叫元素,用逗号隔开。先看四种典型写法:

fruits = ["苹果", "香蕉", "橙子"]     # 3 个元素的列表
empty = []                           # 空列表,先空着,以后往里加
nums = [1, 2, 3, 4, 5]               # 数字列表
mixed = ["小明", 18, 92.5, True]      # 列表里可以装不同类型(但一般不建议混装)

列表有两个关键特点,跟字符串正好一正一反:

特点 说明 例子
有序 元素有固定顺序,按下标取,下标从 0 开始(和字符串一样) ["a", "b"][0]a
可改 列表里的元素能增、能删、能改(和字符串正好相反) fruits[0] = "新东西"

有序和字符串一样,所以昨天学的索引、切片、len(),列表全都能用:

fruits = ["苹果", "香蕉", "橙子"]

print(fruits)         # ['苹果', '香蕉', '橙子']
print(fruits[0])      # 苹果(下标从 0 开始,和字符串一样)
print(fruits[-1])     # 橙子(负数从右往左数)
print(fruits[1:])     # ['香蕉', '橙子'](切片也适用!)
print(len(fruits))    # 3(列表长度 = 元素个数)

输出:

['苹果', '香蕉', '橙子']
苹果
橙子
['香蕉', '橙子']
3

区别只在于:字符串"只能看不能改",列表"能看也能改"。写列表注意三点:

  • 用的是方括号 [],不是小括号 (),也不是花括号 {}——写错类型就变了。
  • 元素之间用英文逗号隔开,中文逗号会让 Python 直接报 SyntaxError
  • 下标越界和字符串一样报错,报的是 IndexError: list index out of range

查:看看列表里有什么

"查"有三种常用方式:按下标取单个、切片取一段、in 判断有没有。配上 len() 数个数:

fruits = ["苹果", "香蕉", "橙子"]

# 方式一:按下标取单个
print(fruits[1])            # 香蕉

# 方式二:in 判断在不在(结果一定是 True / False)
if "苹果" in fruits:
    print("有苹果!")
else:
    print("没有苹果")

# 方式三:len 数有几个
print(f"一共 {len(fruits)} 种水果")

输出:

香蕉
有苹果!
一共 3 种水果

两个小提醒:in 判断的是"有没有这个值",不是下标,所以 2 in fruits 的意思是"列表里有没有 2 这个元素";fruits[3] 在只有 3 个元素的列表里会报 IndexError,取之前先用 len() 确认。判断列表是不是空的,可以写 if len(fruits) == 0:,更地道的写法是直接 if fruits:——空列表在判断里等于 False。

增:往列表里加东西

加东西有两个方法,区别在加的位置:

方法 作用 例子
append(值) 加到末尾(最常用) fruits.append("葡萄")
insert(位置, 值) 插到指定位置(位置从 0 数) fruits.insert(0, "西瓜")
fruits = ["苹果", "香蕉"]

fruits.append("橙子")       # 加在末尾
print(fruits)              # ['苹果', '香蕉', '橙子']

fruits.insert(0, "西瓜")    # 插到第 0 个位置
print(fruits)              # ['西瓜', '苹果', '香蕉', '橙子']

append 配循环,就是购物车的雏形——从用户手里收一串数据:

cart = []                    # 空购物车
while True:
    item = input("输入要买的商品(输入 q 退出):")
    if item == "q":
        break                # 用户说退出,跳出循环
    cart.append(item)        # 把商品加进列表
print(f"你的购物车:{cart}")

假设依次输入 牛奶、面包、q,输出:

输入要买的商品(输入 q 退出):牛奶
输入要买的商品(输入 q 退出):面包
输入要买的商品(输入 q 退出):q
你的购物车:['牛奶', '面包']

这里有几个坑提前说:

  • append 一次只能加一个:fruits.append("a", "b") 会报错。想一次加多个,用循环,或者 extend
  • append 是把整个东西当一个元素加进去:fruits.append(["a", "b"]) 会得到一个"列表套列表",元素本身是个列表。
  • 别忘了 breakwhile True 没有退出条件会永远循环,程序卡死,按 Ctrl + C 强制停止。

删:从列表里拿走东西

删东西有三个方法,按"删谁"来选:

方法 作用 例子
remove(值) 删,删第一个匹配的 fruits.remove("香蕉")
pop() 位置删,默认删最后一个,并返回删掉的值 fruits.pop()fruits.pop(0)
clear() 清空整个列表 fruits.clear()
fruits = ["苹果", "香蕉", "橙子", "葡萄"]

fruits.remove("香蕉")      # 按值删:删掉香蕉
print(fruits)             # ['苹果', '橙子', '葡萄']

last = fruits.pop()       # 删最后一个(葡萄),并且把葡萄接住
print(fruits)             # ['苹果', '橙子']
print(last)               # 葡萄

first = fruits.pop(0)     # 按位置删:删第 0 个(苹果)
print(fruits)             # ['橙子']

pop() 会"顺手"把删掉的值返回给你,这是它和 remove 最大的不同。对照着看更清楚:

cart = ["牛奶", "面包", "牛奶", "鸡蛋"]

cart.remove("牛奶")        # 只删第一个"牛奶"
print(cart)              # ['面包', '牛奶', '鸡蛋']

cart.pop(0)              # 删第 0 个(现在第 0 个是"面包")
print(cart)              # ['牛奶', '鸡蛋']

输出:

['面包', '牛奶', '鸡蛋']
['牛奶', '鸡蛋']

三个容易翻车的地方:

  • remove("不存在的东西") 会报 ValueError: list.remove(x): x not in list。删之前先 in 判断一下。
  • remove 只删第一个匹配的,列表里有两个同名商品,只删一个——上面例子就是证明。
  • pop() 不传参数删最后一个,pop(0) 删第一个——pop 后面跟的是位置(下标),不是值

改:替换列表里的东西

改最简单:直接给某个位置赋值。写法是 列表[下标] = 新值

fruits = ["苹果", "香蕉", "橙子"]

fruits[0] = "大西瓜"       # 把第 0 个(苹果)换成大西瓜
print(fruits)            # ['大西瓜', '香蕉', '橙子']

配合 for 循环,还能批量改——比如给每个同学加 5 分:

scores = [80, 85, 90]
for i in range(len(scores)):
    scores[i] = scores[i] + 5      # 每个人加 5 分
print(scores)                      # [85, 90, 95]

输出:

[85, 90, 95]

几个边界要记清:只能改已存在的位置,fruits[3] = "西瓜" 在只有 3 个元素的列表里会报 IndexError;想加新东西用 append / insert,赋值是"替换"不是"新增"。另外提醒一句:循环里改列表没问题,但循环里删列表元素容易出 bug(下标会错位),新手先别这么干,卡住了问 AI。

遍历:把每个元素过一遍

遍历就是用一个 for 循环,把列表里的元素从头到尾逐个拿出来处理。这是操作列表最核心的动作,没有之一:

fruits = ["苹果", "香蕉", "橙子"]

for f in fruits:          # f 依次等于每个元素
    print(f)

输出:

苹果
香蕉
橙子

遍历有两种常见写法,看你要不要"位置":

scores = [92, 85, 78]

# 写法一:直接拿元素(常用,够用)
for s in scores:
    print(f"成绩:{s}")

# 写法二:拿下标再取元素(需要位置信息时用)
for i in range(len(scores)):
    print(f"第 {i + 1} 个同学:{scores[i]}")

输出:

成绩:92
成绩:85
成绩:78
第 1 个同学:92
第 2 个同学:85
第 3 个同学:78

遍历最常见的搭档是"累加器"模式——在循环外面初始化一个变量,循环里更新它。最典型的例子是求和:

nums = [10, 20, 30, 40]
total = 0
for n in nums:
    total = total + n      # 把每个数累加进 total
print(total)               # 100

输出:

100

统计个数、求和、找最大,全是这个套路:先 total = 0 之类地"起个头",再循环里更新。两个容易混的点:遍历的循环变量名(fsn)随便起,但要见名知意;for i in range(len(fruits)) 里的 i下标fruits[i] 才是元素,别把两者搞混。

排序与统计:数字列表的常用操作

Python 给列表配了一批现成的小工具,写程序做数据分析时天天用:

方法 / 函数 作用 例子 结果
sort() 原地排序(从小到大) [3,1,2].sort() [1,2,3]
reverse() 原地倒序 [1,2,3].reverse() [3,2,1]
sorted(列表) 排序后返回新列表(原列表不动) sorted([3,1,2]) [1,2,3]
max(列表) 最大值 max([3,1,2]) 3
min(列表) 最小值 min([3,1,2]) 1
sum(列表) 求和 sum([3,1,2]) 6
nums = [3, 1, 2]

nums.sort()          # 直接改 nums 自己
print(nums)          # [1, 2, 3]

nums.reverse()       # 倒过来
print(nums)          # [3, 2, 1]

nums = [3, 1, 2]     # 重新来一份
print(sorted(nums))  # [1, 2, 3](sorted 不改变原列表)
print(nums)          # [3, 1, 2](原列表没动)

注意 sort()sorted() 的区别:sort() 直接改原列表,sorted() 不动原列表、返回新的。记不住就统一用 sorted(),更安全。

综合用一下,成绩统计:

scores = [92, 85, 78, 95, 60]

print(f"最高分:{max(scores)}")      # 最高分:95
print(f"最低分:{min(scores)}")      # 最低分:60
print(f"总分:{sum(scores)}")        # 总分:410
print(f"平均分:{sum(scores) / len(scores)}")   # 平均分:82.0

scores.sort()                        # 排序
print(scores)                        # [60, 78, 85, 92, 95](从小到大)

输出:

最高分:95
最低分:60
总分:410
平均分:82.0
[60, 78, 85, 92, 95]

两个边界:sort() 对数字和字符串都能排,但一个列表里混着数字和字符串会报 TypeError: '<' not supportedsum() 只对数字列表有效,字符串列表用 sum 会报错。

列表推导式:一行生成列表(看懂即可)

列表推导式是一种"浓缩写法":用一行代码把"循环 + 生成列表"两件事一起做了。今天不要求会写,看懂、知道让 AI 用就行——面试和读代码时会碰到:

# 普通写法:循环生成 1~5 的平方
squares = []
for x in range(1, 6):
    squares.append(x * x)
print(squares)               # [1, 4, 9, 16, 25]

# 列表推导式:一行搞定(看懂即可)
squares2 = [x * x for x in range(1, 6)]
print(squares2)              # [1, 4, 9, 16, 25]

输出:

[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]

推导式还能加条件——后面跟个 if 就行:

even = [x for x in range(1, 11) if x % 2 == 0]
print(even)                  # [2, 4, 6, 8, 10](1~10 里的偶数)

输出:

[2, 4, 6, 8, 10]

读法是:[表达式 for 变量 in 列表 if 条件],翻译成普通话就是"对列表里每个元素(满足条件的),把表达式的结果收进新列表"。记住两点:别为了炫技硬写推导式——逻辑复杂时普通 for 循环更好读,自己写程序优先用看得懂的写法;看到 AI 生成的推导式看不懂时,让 AI"用普通 for 循环改写",读代码就无压力了。

随堂练习

练习一:搭水果列表

建一个空列表,用 append 依次加入你喜欢的 3 种水果,打印出来;再用 insert 把一种水果插到最前面。

提示:空列表是 fruits = []insert(0, "西瓜") 插到最前面。

练习二:改与删

把列表里第 2 个水果换成别的,删掉第 1 个,最后打印列表;再用 in 判断"你换的水果在不在列表里"。

提示:第 2 个的下标是 1;删第一个用 pop(0);判断用 if x in fruits:

练习三(必做):购物车小程序

写一个小程序:提示用户输入商品名,输入 q 结束,用循环把商品加进列表,最后打印购物车,并打印"一共 N 件商品"。

提示:核心结构是 while True + input + if item == "q": break + cart.append(item)。下午实操必做,第 6 周项目会用到一模一样的逻辑。

进阶题(可选):购物车带统计

在练习三基础上,打印购物车前先排序(cart.sort()),再数一数有没有"牛奶"。

写不出来时,让 AI 搭把手

场景:购物车小程序写不出来,需要 AI 给参考代码
提示词:
我是 Python 初学者,今天学列表。请帮我写一个购物车小程序:
用 while 循环提示用户输入商品名,输入 q 退出,
把商品用 append 加进列表,最后打印购物车和商品数量。
代码加注释,用我能看懂的写法(不要用列表推导式)。
场景:想对列表做某个操作但不会,比如"列表里的数字求和"
提示词:
我有一个数字列表 scores = [92, 85, 78, 95, 60]。
请用最基础的方法(for 循环累加)算出总和和平均分,
不要用 sum() 函数,代码加注释。

常见报错速查

报错信息 什么意思 怎么办
IndexError: list index out of range 下标越界,访问了不存在的元素 检查下标是否超过 len() - 1
AttributeError: 'list' object has no attribute 'push' 调用了不存在的方法 列表添加用 appendpush 是别的语言的习惯
ValueError: list.remove(x): x not in list remove 的东西不在列表里 删之前先用 in 判断,或改用 pop
TypeError: '<' not supported between instances 排序时列表里混了不同类型 保证列表里是同一类型(都是数字或都是字符串)

收个尾

今天动手比较多,总结起来就一张图:增用 append(末尾)/ insert(指定位置);删用 pop(按位置)/ remove(按值);改直接 列表[下标] = 新值;查用下标 + in + for 遍历。遍历是核心动作,配合"累加器"模式能求和、能统计、能找最大;数字列表三件套 max / min / sum,排序用 sort()(改原列表)或 sorted()(返回新列表)。

明天学字典——列表的"带名字版本"。列表里的元素只能靠下标 [0][1] 找,字典里的数据能靠名字找:student["name"]。通讯录、成绩单这类"有名字的数据",字典才是主力。