本周目标
- 掌握列表(List) 的创建、访问、增删改查和切片操作
- 完成第一个综合实战项目:学生成绩管理系统(基础版)
一、为什么需要列表和元组?
前几周我们学的变量,每个只能存储一个值。如果让你管理一个班级50名学生的姓名,难道要定义50个变量吗?student1 = "张三"、student2 = "李四"……显然不现实。
在AI领域,数据从来不是“单个”出现的,而是“批量”出现的:
- 训练数据集: 成千上万张图片的像素值
- 特征向量: 一个样本可能包含几百个特征(如年龄、收入、购买次数等)
- 模型参数: 神经网络的权重矩阵(动辄百万千万级别的参数)
如果没有批量管理数据的能力,AI开发寸步难行。今天我们要学的列表(List)和元组(Tuple),就是Python中最基础、最常用的“数据容器”,专门用来批量存储和管理数据。
二、列表(List)——可变的数据“大仓库”
1. 什么是列表?如何创建?
列表是用方括号 [] 包裹、逗号 , 分隔的一组数据,可以存放任意类型的数据(数字、字符串、布尔值甚至其他列表)。
# 创建空列表empty_list = []# 存放数字scores = [85, 92, 78, 90, 88]# 存放字符串students = ["张三", "李四", "王五"]# 混合类型(Python的列表非常灵活,可以混装)mixed = [10, "Hello", True, 3.14]# 查看列表内容print(scores) # [85, 92, 78, 90, 88]print(type(scores)) # <class 'list'>
2. 访问列表元素——索引(下标)
列表中的每个元素都有一个编号,叫做索引(Index),从 0 开始计数:
students = ["张三", "李四", "王五", "赵六", "孙七"]# 索引: 0 1 2 3 4print(students[0]) # 张三(第一个元素)print(students[2]) # 王五(第三个元素)print(students[-1]) # 孙七(倒数第一个元素,-1表示最后一个)print(students[-2]) # 赵六(倒数第二个元素)
⚠️ 常见错误:访问不存在的索引会报错 IndexError。例如列表长度为5,你却访问 students[5](因为最大索引是4)。
3. 切片(Slicing)——从列表中取出一段
切片是列表最强大的操作之一,语法为:列表[起始:结束:步长],遵循左闭右开原则(包含起始,不包含结束)。
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(nums[2:6]) # [2, 3, 4, 5](索引2到5,不含6)print(nums[:4]) # [0, 1, 2, 3](省略起始,从0开始)print(nums[6:]) # [6, 7, 8, 9](省略结束,取到末尾)print(nums[::2]) # [0, 2, 4, 6, 8](从头到尾,步长为2)print(nums[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0](步长为负,反转列表!)
💡 AI小贴士:在AI数据处理中,切片非常常用。比如把数据集按比例切分成训练集(前80%)和测试集(后20%):train = data[:80]、test = data[80:]。
4. 修改列表——增、删、改
(1)改——直接赋值修改
fruits = ["苹果", "香蕉", "橙子"]fruits[1] = "草莓" # 把索引1的"香蕉"改为"草莓"print(fruits) # ['苹果', '草莓', '橙子']
(2)增——添加元素
fruits = ["苹果", "香蕉"]# append():在末尾添加一个元素fruits.append("橙子")print(fruits) # ['苹果', '香蕉', '橙子']# insert(索引, 元素):在指定位置插入fruits.insert(1, "草莓")print(fruits) # ['苹果', '草莓', '香蕉', '橙子']# extend():扩展另一个列表(合并)more = ["西瓜", "哈密瓜"]fruits.extend(more)print(fruits) # ['苹果', '草莓', '香蕉', '橙子', '西瓜', '哈密瓜']
(3)删——删除元素
fruits = ['苹果', '草莓', '香蕉', '橙子', '西瓜', '哈密瓜']# remove(元素):删除第一个匹配的元素fruits.remove("草莓")print(fruits) # ['苹果', '香蕉', '橙子', '西瓜', '哈密瓜']# pop(索引):删除指定索引的元素,并返回它(不写索引则删除最后一个)removed = fruits.pop(2) # 删除索引2的"橙子"print(removed) # 橙子print(fruits) # ['苹果', '香蕉', '西瓜', '哈密瓜']# del:删除指定索引或整个列表del fruits[1] # 删除索引1的"香蕉"print(fruits) # ['苹果', '西瓜', '哈密瓜']# clear():清空所有元素fruits.clear()print(fruits) # []
5. 遍历列表——和 for 循环结合
在AI开发中,遍历数据集是最常见的操作之一:
# 遍历元素scores = [85, 92, 78, 90, 88]for score in scores: print(score)# 输出:85, 92, 78, 90, 88# 同时获取索引和值(使用 enumerate)for index, score in enumerate(scores): print(f"第{index+1}位学生的成绩:{score}分")# 输出:# 第1位学生的成绩:85分# 第2位学生的成绩:92分# ...
6. 列表常用方法速查表
| | |
|---|
append(x) | | [1,2].append(3) |
insert(i, x) | | [1,3].insert(1,2) |
remove(x) | | [1,2,3].remove(2) |
pop(i) | | [1,2,3].pop(1) |
index(x) | | [1,2,3].index(2) |
count(x) | | [1,2,2,3].count(2) |
sort() | | [3,1,2].sort() |
reverse() | | [1,2,3].reverse() |
copy() | | |
7. ⚠️ 新手特别关注:列表的“引用”陷阱
先来看一个容易踩坑的例子:
# 陷阱1:列表复制a = [1, 2, 3]b = a # 这里不是复制!b和a指向同一个列表b.append(4)print(a) # [1, 2, 3, 4] —— a也被改了!print(b) # [1, 2, 3, 4]# ✅ 正确复制方式:b = a.copy() # 或者 b = a[:]b.append(5)print(a) # [1, 2, 3, 4] —— 不变print(b) # [1, 2, 3, 4, 5]
💡 记住:b = a 只是给原列表贴了个新标签,而不是创建新列表。这在AI中尤其需要注意——比如你不小心把测试集的数据引用改动了,训练集也会跟着变。
三、元组(Tuple)——不可变的“安全箱子”
1. 什么是元组?如何创建?
元组用圆括号 () 包裹,和列表一样可以存放任意数据,但一旦创建,就不能修改(不可变)。
# 创建元组colors = ("红", "绿", "蓝")numbers = (1, 2, 3, 4, 5)single = (5,) # ⚠️ 注意:只有一个元素时,必须加逗号,否则会被当作普通括号运算print(type(colors)) # <class 'tuple'>print(type((5))) # <class 'int'> (没加逗号,被当成了整数5)print(type((5,))) # <class 'tuple'> (加了逗号,才是元组)
2. 元组的访问和遍历(和列表一样)
colors = ("红", "绿", "蓝")print(colors[0]) # 红print(colors[-1]) # 蓝for color in colors: print(color) # 红 绿 蓝
3. 元组的不可变性(关键区别)
colors = ("红", "绿", "蓝")# colors[0] = "黄" # ❌ 报错!TypeError:元组不支持修改
为什么 Python 要设计“不可变”的元组?
- 数据安全: 当作常量使用,防止被意外修改。比如你定义了模型训练的超参数
CONFIG = (0.001, 100, 32)(学习率、轮数、批次大小),不希望程序运行中被改动。 - 字典的键(Key): 只有不可变类型(如字符串、数字、元组)才能作为字典的键,列表不行。
- 性能更高: 元组比列表更轻量,内存占用更小。
4. 元组的常用场景
场景1:函数返回多个值
def get_student_info(): name = "张三" age = 20 score = 95 return name, age, score # 实际返回的是一个元组info = get_student_info()print(info) # ('张三', 20, 95)print(info[0]) # 张三
场景2:AI中的坐标、尺寸等固定参数
# 图像尺寸(宽度,高度)——固定不变image_size = (224, 224) # 常用于ResNet等模型的输入尺寸# RGB颜色值(范围0-255)white = (255, 255, 255)
四、列表与元组的对比总结
五、实战项目:学生成绩管理系统(基础版)
现在,让我们综合运用本周和之前的知识,构建一个真正能用的交互式程序!
功能设计
- 存储所有学生的姓名和成绩(用列表存储,每个学生是一个列表
[姓名, 成绩]) - 提供菜单:添加学生、查看所有成绩、统计总分/平均分、查找最高/最低分、退出
完整代码
# ============================================# 学生成绩管理系统(基础版)# 知识点:列表操作 + 循环 + 条件判断# ============================================# 存储学生数据,格式:[ ["姓名1", 成绩1], ["姓名2", 成绩2], ... ]students = []def show_menu(): """显示菜单""" print("\n" + "=" * 40) print(" 📚 学生成绩管理系统") print("=" * 40) print(" 1. 添加学生成绩") print(" 2. 查看所有成绩") print(" 3. 统计总分和平均分") print(" 4. 查找最高分和最低分") print(" 5. 退出系统") print("=" * 40)def add_student(): """添加学生""" name = input("请输入学生姓名:") # 输入验证:确保成绩是数字 while True: try: score = float(input("请输入成绩:")) break except ValueError: print("❌ 成绩必须是数字,请重新输入!") students.append([name, score]) print(f"✅ 学生 {name}({score}分)添加成功!")def show_all(): """查看所有成绩""" if not students: print("⚠️ 当前没有学生数据,请先添加!") return print("\n---------- 成绩列表 ----------") print("序号\t姓名\t成绩") print("-" * 30) # enumerate 同时获取索引和值 for i, stu in enumerate(students, start=1): print(f"{i}\t{stu[0]}\t{stu[1]}") print("-" * 30) print(f"共 {len(students)} 名学生")def show_stats(): """统计总分和平均分""" if not students: print("⚠️ 当前没有学生数据,请先添加!") return total = 0 for stu in students: total += stu[1] avg = total / len(students) print(f"\n📊 统计结果:") print(f" 总人数:{len(students)}") print(f" 总 分:{total:.1f}") print(f" 平均分:{avg:.1f}")def show_min_max(): """查找最高分和最低分""" if not students: print("⚠️ 当前没有学生数据,请先添加!") return # 假设第一个学生既是最高也是最低 max_stu = students[0] min_stu = students[0] for stu in students[1:]: # 从第二个开始遍历 if stu[1] > max_stu[1]: max_stu = stu if stu[1] < min_stu[1]: min_stu = stu print(f"\n🏆 最高分:{max_stu[0]}({max_stu[1]}分)") print(f"📉 最低分:{min_stu[0]}({min_stu[1]}分)")# ========== 主程序入口 ==========print("👋 欢迎使用学生成绩管理系统!")while True: show_menu() choice = input("请选择操作(1-5):") if choice == "1": add_student() elif choice == "2": show_all() elif choice == "3": show_stats() elif choice == "4": show_min_max() elif choice == "5": print("👋 感谢使用,再见!") break else: print("❌ 无效输入,请输入 1-5 之间的数字!")
运行效果示例:
👋 欢迎使用学生成绩管理系统!======================================== 📚 学生成绩管理系统======================================== 1. 添加学生成绩 2. 查看所有成绩 3. 统计总分和平均分 4. 查找最高分和最低分 5. 退出系统========================================请选择操作(1-5):1请输入学生姓名:林小夕请输入成绩:95✅ 学生 林小夕(95.0分)添加成功!
六、动手练习
练习1:列表基础操作
现有列表 nums = [10, 20, 30, 40, 50],请依次完成:
👆 点击查看参考答案nums = [10, 20, 30, 40, 50]nums.append(60) # [10,20,30,40,50,60]nums.insert(2, 25) # [10,20,25,30,40,50,60]nums.remove(40) # [10,20,25,30,50,60]nums.reverse() # [60,50,30,25,20,10]print(nums)
练习2:求列表中的最大值和最小值(禁止用内置 max/min)
编写函数,手动实现查找最大值和最小值的功能。
👆 点击查看参考答案data = [3, 7, 2, 9, 1, 5]max_val = data[0]min_val = data[0]for num in data: if num > max_val: max_val = num if num < min_val: min_val = numprint(f"最大值:{max_val},最小值:{min_val}") # 最大值:9,最小值:1
练习3:列表去重
给定列表 [1, 2, 2, 3, 4, 4, 4, 5],编写程序去除重复元素,保留原始顺序。
👆 点击查看参考答案original = [1, 2, 2, 3, 4, 4, 4, 5]unique = []for num in original: if num not in unique: # 如果不在新列表中,才添加 unique.append(num)print(unique) # [1, 2, 3, 4, 5]#
练习4:元组解包(Unpacking)
有一个元组 person = ("张三", 25, "北京"),请将其解包为三个独立的变量 name、age、city,然后打印输出。
👆 点击查看参考答案person = ("张三", 25, "北京")name, age, city = personprint(f"姓名:{name},年龄:{age},城市:{city}")# 输出:姓名:张三,年龄:25,城市:北京
练习5(AI场景模拟):特征向量标准化
假设你有一组样本的特征数据(每个样本包含3个特征),存储为列表的列表:
samples = [ [2.5, 3.0, 1.2], [1.8, 2.9, 1.5], [3.1, 3.2, 0.8], [2.0, 2.5, 1.0]]
请编写程序,计算每个特征(列)的平均值,并输出。
👆 点击查看参考答案samples = [ [2.5, 3.0, 1.2], [1.8, 2.9, 1.5], [3.1, 3.2, 0.8], [2.0, 2.5, 1.0]]# 假设所有样本长度一致num_features = len(samples[0])sums = [0] * num_features # 初始化三个0,对应三个特征for sample in samples: for i in range(num_features): sums[i] += sample[i]averages = [s / len(samples) for s in sums] # 列表推导式(下周会讲)print("每个特征的平均值:", averages)# 输出:[2.35, 2.9, 1.125]
本周小结
本周我们学会了用“批量”的方式管理数据:
| |
|---|
| 列表(List) | 用 [] 定义,可变(可增删改),是最常用的数据容器 |
| 索引与切片 | 索引从0开始,切片 [start:end:step] 遵循左闭右开 |
| 列表的增删改 | append()、insert()、remove()、pop()、del |
| 遍历列表 | for item in list: 或 for i, item in enumerate(list): |
| 引用陷阱 | b = a 不复制列表,要复制用 a.copy() 或 a[:] |
| 元组(Tuple) | |
| 元组解包 | a, b, c = (1, 2, 3) |
下期预告:我们将继续学习另外两种重要的数据容器——字典(Dict)和集合(Set)。字典是“键值对”的存储方式,在AI中用来保存配置参数、特征映射等场景非常方便。我们还会做一个“单词词频统计器”的实战项目!
📌 本周作业建议:
- 把“学生成绩管理系统”完整敲一遍并运行,然后尝试扩展功能——比如增加“按成绩排序输出”功能。
- 在 Jupyter Notebook 中反复练习切片的操作,直到形成肌肉记忆(切片在AI数据预处理中太常用了)。
- 思考:为什么在AI中,图像数据经常被存储为多维列表(例如
[[[R,G,B], ...], ...])?这和我们今天学的列表嵌套列表有什么关系?
列表是你在Python生涯中打交道最多的数据类型之一,务必熟练掌握!千里之行,始于足下。 🐍