第4章 4.1 列表 list 基础与切片

🎯 开场:为什么你离不开列表?

想象这个场景:你在淘宝加了 7 件商品到购物车,突然发现第 3 件降价了,你想把它排到最前面优先下单。

如果只用变量,你需要 7 个变量:商品1商品2...光想想就头皮发麻。

更崩溃的是,你还想:
- 按价格排序
- 删除已下架的商品
- 把两单合并

这就是列表(list)存在的意义。

上一章我们学会了写工具函数,这一章我们学一个真正能装东西的「口袋」——列表。学会它,你就能像整理衣柜一样,优雅地管理一堆数据。


🧱 基础 25 分钟:列表是什么?怎么用?

1. 列表的创建:先有个"口袋"

# 创建一个空列表(相当于买个购物袋)
购物车 = []

# 直接往里塞东西(最常用的方式)
购物车 = ["iPhone15", "AirPods", "MacBook Pro"]

# 用 list() 把其他东西转成列表
价格 = list(range(100, 106))  # [100, 101, 102, 103, 104, 105]
print(价格)

运行结果:

[100, 101, 102, 103, 104, 105]

说白了: 列表就是有序的、可变的、数据容器。像一排连续编号的抽屉,你想放什么就放什么。

2. 列表的"增删改查":四件套

生活里管衣柜就靠这四件事:放进去、拿出来、换一件、查一下有没有。列表也是一样。

查:按下标找东西(从 0 开始数)

购物车 = ["iPhone15", "AirPods", "MacBook Pro", "iPad"]

# 第一个元素
print(购物车[0])    # iPhone15

# 最后一个元素(反向索引,从 -1 开始)
print(购物车[-1])   # iPad

# 越界了会报错!
# print(购物车[10])  # IndexError: list index out of range

注意! Python 从 0 开始数,不是 1。所以第 1 个元素的下标是 0,第 2 个是 1。

配图1 - 配图1

增:往列表里加东西

购物车 = ["iPhone15", "AirPods"]

# append() - 往末尾加一个(最常用)
购物车.append("Apple Watch")
print(购物车)  # ['iPhone15', 'AirPods', 'Apple Watch']

# insert() - 往指定位置插一个
购物车.insert(1, "iPad")  # 插到下标1的位置
print(购物车)  # ['iPhone15', 'iPad', 'AirPods', 'Apple Watch']

# extend() - 一次性往末尾加一堆
购物车.extend(["数据线", "手机壳"])
print(购物车)  # ['iPhone15', 'iPad', 'AirPods', 'Apple Watch', '数据线', '手机壳']

类比: append 像是往购物车丢一件新商品,extend 像是一次性把整个购物车的商品倒进去。

删:把东西拿走

购物车 = ["iPhone15", "AirPods", "iPad", "AirPods"]

# pop() - 移除并返回最后一个(不指定索引)
最后一件 = 购物车.pop()
print(最后一件)   # AirPods
print(购物车)     # ['iPhone15', 'AirPods', 'iPad']

# pop(0) - 移除并返回指定位置的
购物车.pop(0)  # 移除 iPhone15
print(购物车)   # ['AirPods', 'iPad']

# remove() - 移除第一个匹配的值(不知道索引时用)
购物车.remove("iPad")
print(购物车)   # ['AirPods']

# 再次 remove("不存在的") 会报错
# 购物车.remove("不存在的")  # ValueError: list.remove(x): x not in list

坑来了! remove() 只删第一个匹配的值。如果列表里有多个 "AirPods",它只删前面那个。

改:换个新的

购物车 = ["iPhone15", "AirPods", "iPad"]

# 直接赋值覆盖
购物车[0] = "iPhone15 Pro"
print(购物车)  # ['iPhone15 Pro', 'AirPods', 'iPad']

3. 切片:一次拿出多个(列表的精髓!)

切片是列表最强大的功能。你可以把它想象成「切面包」——从一整条面包上切下一段。

商品列表 = ["手机", "电脑", "平板", "手表", "耳机", "音响"]

# 切前3个 [start:end],注意 end 不包含!
前3个 = 商品列表[0:3]
print(前3个)  # ['手机', '电脑', '平板']

# 简写形式(省掉 0)
前3个 = 商品列表[:3]
print(前3个)  # ['手机', '电脑', '平板']

# 切后3个(省掉 end 默认到末尾)
后3个 = 商品列表[3:6]
后3个 = 商品列表[3:]  # 同样效果
print(后3个)  # ['手表', '耳机', '音响']

# 每隔一个取一个(step=2)
每隔一个 = 商品列表[::2]
print(每隔一个)  # ['手机', '平板', '耳机']

# 倒着取(step=-1)
倒序 = 商品列表[::-1]
print(倒序)  # ['音响', '耳机', '手表', '平板', '电脑', '手机']

# 复制整个列表(常用!)
备份 = 商品列表[:]
print(备份)  # ['手机', '电脑', '平板', '手表', '耳机', '音响']

核心公式: 列表[start:end:step]
- start:从哪开始(默认 0)
- end:到哪结束(不包含,默认到末尾)
- step:步长(默认 1,-1 表示倒序)

配图说明:切片原理图,start=0, end=4, step=1 切出['手机','电脑','平板','手表']


4. 其他常用操作

商品 = ["手机", "电脑", "平板", "手表"]

# 长度:一共有多少个
print(len(商品))  # 4

# 排序(原地修改,升序)
价格 = [2999, 5999, 8999, 1999]
价格.sort()
print(价格)  # [1999, 2999, 5999, 8999]

# 降序排序
价格.sort(reverse=True)
print(价格)  # [8999, 5999, 2999, 1999]

# 查找元素的索引(找不到会报错)
平板索引 = 商品.index("平板")
print(平板索引)  # 2

# 统计元素出现次数
重复列表 = [1, 2, 2, 3, 2, 4]
print(重复列表.count(2))  # 3

# 判断元素是否在列表中
print("手机" in 商品)  # True
print("相机" in 商品)  # False

🔥 实战 35 分钟:3 个递进小项目

项目 1(5 分钟):待办清单管理器

需求: 添加任务、查看任务、完成任务、删除任务。

待办清单 = []

while True:
print("\n===== 待办清单 =====")
print(f"当前有 {len(待办清单)} 个任务:")
for i, 任务 in enumerate(待办清单, 1):
    print(f"  {i}. {任务}")
print("===================")
print("1. 添加任务")
print("2. 完成任务")
print("3. 删除任务")
print("4. 退出")

选择 = input("请选择:")

if 选择 == "1":
    任务 = input("输入新任务:")
    待办清单.append(任务)
    print(f"✅ 已添加:{任务}")
elif 选择 == "2":
    编号 = int(input("输入要完成的任务编号:")) - 1
    if 0 <= 编号 < len(待办清单):
        完成的任务 = 待办清单.pop(编号)
        print(f"🎉 已完成:{完成的任务}")
    else:
        print("❌ 编号无效")
elif 选择 == "3":
    编号 = int(input("输入要删除的任务编号:")) - 1
    if 0 <= 编号 < len(待办清单):
        删除的任务 = 待办清单.pop(编号)
        print(f"🗑️ 已删除:{删除的任务}")
    else:
        print("❌ 编号无效")
elif 选择 == "4":
    print("拜拜!")
    break

预期输出:

===== 待办清单 =====
当前有 0 个任务:
===================
1. 添加任务
2. 完成任务
3. 删除任务
4. 退出
请选择:1
输入新任务:买牛奶
✅ 已添加:买牛奶

一句话解释:append 添加任务,用 pop 删除指定位置的任务,用 enumerate 同时遍历索引和值。


项目 2(15 分钟):CSV 文件数据清洗

需求: 读取一个 CSV 文件(模拟),筛选出大于 5000 元的订单,按金额降序排列。

# 模拟 CSV 数据(实际开发中会用 csv 模块或 pandas)
订单数据 = [
{"订单号": "A001", "商品": "iPhone", "金额": 8999},
{"订单号": "A002", "商品": "数据线", "金额": 59},
{"订单号": "A003", "商品": "MacBook", "金额": 15999},
{"订单号": "A004", "商品": "手机壳", "金额": 39},
{"订单号": "A005", "商品": "AirPods", "金额": 1999},
{"订单号": "A006", "商品": "iPad", "金额": 5999},
]

# 步骤1:筛选金额大于 5000 的订单
大额订单 = [订单 for 订单 in 订单数据 if 订单["金额"] > 5000]
print(f"筛选出 {len(大额订单)} 个大额订单:")
for 订单 in 大额订单:
print(f"  {订单['订单号']}: {订单['商品']} - ¥{订单['金额']}")

# 步骤2:按金额降序排列
大额订单.sort(key=lambda x: x["金额"], reverse=True)
print("\n按金额降序:")
for 订单 in 大额订单:
print(f"  {订单['订单号']}: {订单['商品']} - ¥{订单['金额']}")

# 步骤3:计算总金额
总金额 = sum(订单["金额"] for 订单 in 大额订单)
print(f"\n大额订单总金额:¥{总金额}")

预期输出:

筛选出 3 个大额订单:
A001: iPhone - ¥8999
A003: MacBook - ¥15999
A006: iPad - ¥5999

按金额降序:
A003: MacBook - ¥15999
A001: iPhone - ¥8999
A006: iPad - ¥5999

大额订单总金额:¥33997

一句话解释: 用列表推导式 [x for x in ... if ...] 快速筛选数据,用 sort(key=..., reverse=...) 指定排序规则。


项目 3(15 分钟):个人支出记录工具

需求: 记录每天支出,按周汇总统计,找出花销最大的分类。

# 模拟一周的支出记录
支出记录 = [
{"日期": "2024-01-15", "分类": "餐饮", "金额": 45},
{"日期": "2024-01-15", "分类": "交通", "金额": 8},
{"日期": "2024-01-16", "分类": "餐饮", "金额": 120},
{"日期": "2024-01-16", "分类": "购物", "金额": 599},
{"日期": "2024-01-17", "分类": "餐饮", "金额": 68},
{"日期": "2024-01-17", "分类": "娱乐", "金额": 200},
{"日期": "2024-01-18", "分类": "交通", "金额": 15},
{"日期": "2024-01-18", "分类": "购物", "金额": 1299},
{"日期": "2024-01-19", "分类": "餐饮", "金额": 55},
{"日期": "2024-01-19", "分类": "娱乐", "金额": 88},
]

# 1. 统计每个分类的总支出
分类统计 = {}
for 记录 in 支出记录:
分类 = 记录["分类"]
金额 = 记录["金额"]
分类统计[分类] = 分类统计.get(分类, 0) + 金额

print("=== 分类支出统计 ===")
for 分类, 总金额 in sorted(分类统计.items(), key=lambda x: x[1], reverse=True):
print(f"{分类}: ¥{总金额}")

# 2. 找出花销最大的分类
最大分类 = max(分类统计.items(), key=lambda x: x[1])
print(f"\n💰 花销最大的是「{最大分类[0]}」,共 ¥{最大分类[1]}")

# 3. 按日期分组(切片练习:每3天一组)
所有日期 = list(set(记录["日期"] for 记录 in 支出记录))
所有日期.sort()
print(f"\n=== 支出日期 ===")
print(所有日期)

# 4. 本周总支出
总支出 = sum(记录["金额"] for 记录 in 支出记录)
日均支出 = 总支出 / len(所有日期)
print(f"\n本周总支出: ¥{总支出},日均 ¥{日均支出:.2f}")

# 5. 添加一条新记录(append 的真实场景)
新记录 = {"日期": "2024-01-20", "分类": "餐饮", "金额": 38}
支出记录.append(新记录)
print(f"\n已添加新记录,当前共 {len(支出记录)} 条记录")

预期输出:

=== 分类支出统计 ===
购物: ¥1898
餐饮: ¥326
娱乐: ¥288
交通: ¥23

💰 花销最大的是「购物」,共 ¥1898

=== 支出日期 ===
['2024-01-15', '2024-01-16', '2024-01-17', '2024-01-18', '2024-01-19']

本周总支出: ¥2535,日均 ¥507.00

已添加新记录,当前共 11 条记录

一句话解释: 用字典配合列表做统计,用 append 添加新记录——这是数据分析最常见的模式。


💪 进阶 20 分钟:常见坑 + 性能 + 调试

坑 1:赋值不是拷贝(浅拷贝陷阱)

# ❌ 错误示范:两个变量指向同一个列表
原列表 = [1, 2, 3]
副本 = 原列表      # 这只是复制了引用!
副本.append(4)
print(原列表)      # [1, 2, 3, 4]  原列表也被改了!

# ✅ 正确做法:真正复制一份
原列表 = [1, 2, 3]
副本 = 原列表[:]   # 切片复制
副本.append(4)
print(原列表)      # [1, 2, 3]  原列表不受影响
print(副本)       # [1, 2, 3, 4]

坑来了! 列表是引用类型,赋值只是复制了"地址",不是数据本身。


坑 2:修改列表时不要边遍历边删除

# ❌ 错误示范:一边遍历一边 remove
数字 = [1, 2, 2, 3, 2, 4]
for n in 数字:
if n == 2:
    数字.remove(n)  # 可能漏删或报错!
print(数字)  # [1, 3, 2, 4]  漏了一个 2

# ✅ 正确做法:创建新列表
数字 = [1, 2, 2, 3, 2, 4]
数字 = [n for n in 数字 if n != 2]  # 列表推导式
print(数字)  # [1, 3, 4]

坑 3:列表的 +extend 不一样

# + 返回新列表,不修改原列表
a = [1, 2]
b = [3, 4]
c = a + b
print(a)  # [1, 2]  原列表不变!
print(b)  # [3, 4]
print(c)  # [1, 2, 3, 4]

# extend 修改原列表
a = [1, 2]
a.extend([3, 4])
print(a)  # [1, 2, 3, 4]  原列表被改了

坑 4:sort() 原地修改,sorted() 返回新列表

# sort() 直接修改原列表
a = [3, 1, 2]
a.sort()
print(a)  # [1, 2, 3]

# sorted() 不修改原列表,返回新列表
a = [3, 1, 2]
b = sorted(a)
print(a)  # [3, 1, 2]  原列表不变
print(b)  # [1, 2, 3]

坑 5:列表可以嵌套,但小心访问越界

# 二维列表(列表里套列表)
矩阵 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 访问第2行第3列
print(矩阵[1][2])  # 6

# ❌ 越界会报错
# print(矩阵[3][0])  # IndexError: list index out of range

性能小贴士:频繁在头部插入用 deque

# ❌ 效率低:往列表头部插入是 O(n) 复杂度
列表 = [1, 2, 3]
列表.insert(0, 0)  # 每次都要挪动所有元素

# ✅ 效率高:用 deque 的 appendleft
from collections import deque
队列 = deque([1, 2, 3])
队列.appendleft(0)  # O(1) 复杂度
print(list(队列))  # [0, 1, 2, 3]

调试技巧:print 大法 + pdb 断点

# 技巧1:print 打印关键变量
商品列表 = ["手机", "电脑", "平板"]
print(f"调试:当前列表 = {商品列表}")

# 技巧2:用 pdb 设置断点(正式调试用)
import pdb
商品列表 = ["手机", "电脑", "平板"]
pdb.set_trace()  # 程序会在这里暂停,可以检查变量
商品列表.append("手表")
print(商品列表)
# 在 pdb 里输入:n(下一行)、p 商品列表(打印变量)、c(继续)

✏️ 练习题

练习 1(2 分钟):基础查操作
- 输入:[10, 20, 30, 40, 50],取第 3 个元素
- 预期输出:30
- 提示:索引从 0 开始

练习 2(2 分钟):基础增删
- 输入:在列表 ["a", "b", "c"] 末尾添加 "d",然后删除第一个元素
- 预期输出:['b', 'c', 'd']
- 提示:append 添加到末尾,pop(0) 删除第一个

练习 3(3 分钟):切片操作
- 输入:对 [1, 2, 3, 4, 5] 每隔一个取一个
- 预期输出:[1, 3, 5]
- 提示:列表[::2] 步长为 2

练习 4(5 分钟):列表推导式
- 输入:将 [1, 2, 3, 4, 5] 中大于 2 的数乘以 10
- 预期输出:[30, 40, 50]
- 提示:[x*10 for x in 列表 if x > 2]

练习 5(5 分钟):报错分析
- 输入:分析下面代码为什么会报错

列表 = [1, 2, 3]
列表[5] = 10
  • 预期输出:解释错误原因
  • 提示:索引 5 越界了

作业:做一个「个人读书笔记管理工具」

  • 需求描述: 管理你读过的书,每本书记录:书名、作者、评分(1-5)、阅读日期、简短感想
  • 功能点:
    1. 添加新书籍记录
    2. 按评分筛选高评分书籍(≥4分)
    3. 按书名排序并显示
    4. 删除指定书名的记录
    5. 统计平均评分
  • 加分项:
    1. 支持从文件读取/保存数据(JSON 格式)
    2. 支持按作者筛选
  • 验收标准: 能跑起来,输入/输出符合预期,代码有注释
  • 提交方式: 评论区贴代码或 GitHub 链接

📚 总结

这一章我们学了列表的增删改查四件套切片操作。列表是 Python 里最常用的数据结构,学好它,后面的字典、文件操作、数据分析都能事半功倍。

下一章我们要学一个和列表很像、但「只读不写」的数据结构——元组。它看起来很简单,但在函数返回值、多值赋值、默认参数等场景下,用处大到你离不开它。


推荐资源:
- Python 官方文档 - 列表(最权威)
- 《Python Crash Course》第 4 章(入门友好)
- 视频:B 站「小甲鱼」Python 教程第 38-40 讲


互动钩子: 你有没有用列表管理过什么有趣的东西?购物清单、追剧列表、还是游戏装备?评论区聊聊,老粉优先回复!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。