Python第四课:循环与数据结构深度解析
文章目录
引言
想象一下,你要管理一个繁忙的快递站
- 列表(List) 就像那一排排整齐的货架,每个格子上放着一个包裹(元素),你有它们的顺序编号(索引)。你可以快速找到第5个货架上的东西,也可以轻松地在末尾新增一个包裹
- 字典(Dictionary) 则像是一个智能查询系统。每个包裹都有一个独一无二的单号(键),通过这个单号,你能瞬间查到它的所有详细信息(值)。找“QG211314”这个包裹?不需要知道它放在第几个,直接问系统就行
那么循环呢?
for循环 就像你沿着货架从头到尾依次检查每一个包裹(“遍历”),这是你知道要处理所有物品时的首选while循环 则像是一个“直到…才停止”的指令。比如,“直到货架清空,才停止搬运”。你并不一开始就知道要循环多少次,而是由一个条件来决定的
在 Python 的世界里,几乎每一个有趣的项目,都离不开这四者的精妙配合。本文将带你深入这个“快递站”,掌握让数据流转自如的核心法则
在开始之前先检查一下你的装备吧!!!
python环境不会装的看这里:从安装到Hello World:Python环境搭建完整指南
python编辑器不会装的看这里:零基础Python入门:手把手教你安装Python、新版PyCharm和VS Code
列表(List)
1. 拿生活类比
想象一下你去超市购物的情景。你推着一辆购物车,里面按顺序放着:牛奶、面包、鸡蛋、苹果
在Python中,这个购物车就是一个列表(List):
shopping_list =['牛奶','面包','鸡蛋','苹果']列表的本质:一个有序、可变的元素集合,像是给你的数据物品准备的一排编号货架
2. 创建列表的多种方式
- 方法3:空列表(准备往里面装东西)
方法2:使用list() 函数
chars =list('Python')# ['P', 'y', 't', 'h', 'o', 'n'] range_list =list(range(5))# [0, 1, 2, 3, 4]方法1:直接创建(最常用)
fruits =['苹果','香蕉','橙子'] numbers =[1,2,3,4,5] mixed =[1,'hello',3.14,True]# 列表可以包含不同类型的元素3. 访问列表元素
列表使用索引(index) 来定位元素,索引从0开始!
colors =['红','橙','黄','绿','青','蓝','紫']# 正向索引(从前往后)print(colors[0])# '红' - 第一个元素print(colors[3])# '绿' - 第四个元素# 负向索引(从后往前)print(colors[-1])# '紫' - 最后一个元素print(colors[-2])# '蓝' - 倒数第二个# 注意:索引越界会报错!# print(colors[7]) # ❌ IndexError: list index out of range小技巧:使用len()函数获取列表长度
length =len(colors)# 7 last_index = length -1# 64. 列表的"增删改查"操作
增加元素:
todo_list =['学习Python','玩游戏']# 在末尾添加 todo_list.append('运动')# ['学习Python', '玩游戏', '运动']# 在指定位置插入 todo_list.insert(1,'吃午饭')# ['学习Python', '吃午饭', '玩游戏', '运动']# 合并另一个列表 todo_list.extend(['阅读','休息'])# 现在有6个任务了删除元素:
# 按值删除(删除第一个匹配项) todo_list.remove('吃午饭')# 按索引删除del todo_list[0]# 删除第一个 popped_item = todo_list.pop()# 删除并返回最后一个 popped_item = todo_list.pop(1)# 删除并返回索引1的元素# 清空列表 todo_list.clear()# 列表变空:[]修改元素:
colors =['红','橙','黄'] colors[1]='粉红'# 直接赋值修改# 现在colors是:['红', '粉红', '黄']查找元素:
colors =['红','橙','黄','绿','蓝']# 检查元素是否存在if'绿'in colors:print("绿色在列表中!")# 获取元素索引 index = colors.index('黄')# 2# colors.index('紫') # ❌ 如果不存在会报错# 统计出现次数 count = colors.count('红')# 15. 切片与遍历
切片(Slicing) - 获取子列表:
numbers =[0,1,2,3,4,5,6,7,8,9]# 基本格式:list[start:end:step]print(numbers[2:6])# [2, 3, 4, 5] # 包含2,不包含6print(numbers[:5])# [0, 1, 2, 3, 4] # 从头开始print(numbers[5:])# [5, 6, 7, 8, 9] # 到末尾结束print(numbers[::2])# [0, 2, 4, 6, 8] # 每隔一个取一个print(numbers[::-1])# 反转列表![9, 8, 7, ... , 0]# 切片创建的是新列表,不影响原列表 first_five = numbers[:5]# 前五个元素的副本遍历列表:
# 方法1:直接遍历元素 fruits =['苹果','香蕉','橙子']for fruit in fruits:print(f"我喜欢吃{fruit}")# 方法2:同时获取索引和元素for index, fruit inenumerate(fruits):print(f"第{index}个水果是:{fruit}")# 输出:第0个水果是:苹果...6. 动手练习
来尝试一下吧,试着完成下面的小挑战:
单词频率统计(为学习字典做准备) text ="apple banana apple orange banana apple" words = text.split()# 统计每个单词出现的次数字典(Dictionary)
1. 从列表的局限到字典的诞生
咱们刚刚学了列表,想象这样一个场景:你需要管理一个班级的学生信息。用列表可以这样写:
students =['张三',18,'男','李四',19,'女','王五',20,'男']问题来了:你怎么快速找到"李四"的年龄?你得记住年龄在列表中的位置,然后通过students[4]来获取。如果数据再多一些,这种"位置记忆"就变得极其困难
字典就是为了解决这个问题而生的:
student ={'姓名':'张三','年龄':18,'性别':'男'}字典的本质:一种键值对(key-value) 的映射结构,让你能通过一个有意义的关键词(键) 快速找到对应的数据(值)
2. 拿生活类比
现实世界的字典
就像查英语字典:
- 键(Key) = 要查的单词(如"apple")
- 值(Value) = 单词的解释(如"苹果")
快递取件系统
- 键(Key) = 快递单号(如"SF123456")
- 值(Value) = 快递信息(收货人、地址、包裹内容)
在Python中,字典就是这样一个高效的查询系统
3. 创建字典的各种方法
方法1:花括号直接创建(最常用)
student ={'姓名':'张三','年龄':18,'专业':'计算机'} book ={'标题':'Python编程','价格':89.9,'库存':True}方法2:dict()函数创建
empty_dict =dict()# 空字典 person =dict(name='李四', age=25)# 关键字参数方式方法3:从列表的元组对创建
pairs =[('姓名','王五'),('年龄',20)] person =dict(pairs)# {'姓名': '王五', '年龄': 20}方法4:字典推导式(后面会详讲)
squares ={x: x**2for x inrange(5)}# {0:0, 1:1, 2:4, 3:9, 4:16}特殊:键的类型(必须是不可变类型)
valid_keys ={'字符串':'value1',# ✅ 字符串123:'value2',# ✅ 整数(1,2,3):'value3',# ✅ 元组(不可变)# ['列表']: 'value4' # ❌ 列表不能作为键(可变)}4. 访问字典
基本访问方式:
student ={'姓名':'张三','年龄':18,'专业':'计算机'}# 通过键访问值(最常用)print(student['姓名'])# '张三'print(student['年龄'])# 18# ❌ 访问不存在的键会报错!# print(student['地址']) # KeyError: '地址'最保险的访问方法:
方法1:get()方法
address = student.get('地址')# 不存在返回None address = student.get('地址','未填写')# 不存在返回默认值'未填写'方法2:in关键字检查键是否存在
'地址'in student # 存在返回True,不存在返回False方法3:setdefault() - 获取值,如果不存在则设置默认值(非常实用!一行代码完成"如果不存在则初始化")
student.setdefault('成绩',[])# 如果'成绩'不存在,则设为空列表[] student['成绩'].append(95)# 现在可以安全地添加成绩5. 字典的"增删改查"操作
增加/修改元素
config ={'主题':'暗色','语言':'中文'}# 添加新键值对 config['字体大小']=14# 现在config: {'主题': '暗色', '语言': '中文', '字体大小': 14}# 修改现有键的值 config['主题']='亮色'# 现在config: {'主题': '亮色', '语言': '中文', '字体大小': 14}# 批量更新(合并字典) config.update({'语言':'英文','声音':True})# 现在config包含了所有更新删除元素
方法1:del语句
del config['字体大小']方法2:pop() - 删除并返回值
language = config.pop('语言')# '英文'方法3:popitem() - 删除最后插入的键值对(Python 3.7+有序)
last_item = config.popitem()# 可能是('声音', True)方法4:clear() - 清空字典
config.clear()# {}获取字典信息
employee ={'姓名':'王五','部门':'技术部','工号':1001}# 获取所有键 keys = employee.keys()# dict_keys(['姓名', '部门', '工号'])# 获取所有值 values = employee.values()# dict_values(['王五', '技术部', 1001])# 获取所有键值对 items = employee.items()# dict_items([('姓名', '王五'), ...])# 注意:这些返回的是视图对象,会随字典变化# 如果需要列表,可以转换: key_list =list(employee.keys())6. 字典的高级技巧
字典推导式
# 创建一个数字到平方的映射 squares ={x: x**2for x inrange(1,6)}# {1:1, 2:4, 3:9, 4:16, 5:25}# 过滤字典 scores ={'张三':85,'李四':92,'王五':78,'赵六':95} high_scores ={name: score for name, score in scores.items()if score >=90}# {'李四': 92, '赵六': 95}# 键值互换(注意值必须唯一) inverted ={score: name for name, score in scores.items()}字典合并(Python 3.9+)
# Python 3.9+ 的新语法 dict1 ={'a':1,'b':2} dict2 ={'b':3,'c':4} merged = dict1 | dict2 # {'a':1, 'b':3, 'c':4}# 注意:相同键时,后面的值覆盖前面的默认字典(defaultdict)
from collections import defaultdict # 传统方式:统计单词频率 words =['apple','banana','apple','orange','banana','apple'] word_count ={}for word in words:if word in word_count: word_count[word]+=1else: word_count[word]=1# 使用defaultdict:自动初始化 word_count = defaultdict(int)# int()默认返回0for word in words: word_count[word]+=1# 不需要检查key是否存在!# defaultdict(<class 'int'>, {'apple':3, 'banana':2, 'orange':1})7. 字典 vs 列表:何时选择谁?
| 特性 | 列表(List) | 字典(Dictionary) |
|---|---|---|
| 核心概念 | 有序集合 | 键值对映射 |
| 访问方式 | 数字索引(0,1,2…) | 任意不可变键 |
| 顺序 | 保持插入顺序(重要!) | Python 3.7+保持插入顺序 |
| 查找速度 | O(n)(线性搜索) | O(1)(平均,哈希表) |
| 内存占用 | 较小 | 较大(需要存储键和值) |
| 典型用途 | 有序数据序列 | 关联数据、快速查找 |
选择指南:
- 需要维护顺序且通过位置访问 → 列表
- 需要通过特定标识符快速查找 → 字典
- 数据是同质的(同一类型) → 考虑列表
- 数据是异质的(不同类型) → 考虑字典
8. 动手练习
数据分析挑战
# 给定销售数据,计算: sales =[{'product':'A','amount':100,'month':'Jan'},{'product':'B','amount':150,'month':'Jan'},{'product':'A','amount':200,'month':'Feb'},{'product':'C','amount':50,'month':'Feb'},]# 1. 每种产品的总销售额# 2. 每个月的总销售额# 3. 最畅销的产品for循环
1. 从重复劳动到自动化
想象一下,你需要给公司100名员工发送生日祝福邮件。手动操作需要:
- 找到第一个员工的邮箱 → 写邮件 → 发送
- 找到第二个员工的邮箱 → 写邮件 → 发送
- …(重复98次)
这种重复模式在编程中太常见了!幸运的是,Python为我们提供了 for循环——让计算机自动处理重复任务的完美工具
# 手动方式(不可行)print("你好,员工1!")print("你好,员工2!")# ... 要写98行print# for循环方式(自动化)for 员工编号 inrange(1,101):print(f"你好,员工{员工编号}!")for循环的本质:一种确定性循环,用于遍历序列中的每个元素并执行相同的操作
2. for循环的基本语法
最简形式
for 变量 in 可迭代对象:# 缩进的代码块会被重复执行 执行操作(变量)一个具体例子
# 遍历水果列表 fruits =['苹果','香蕉','橙子','葡萄']for fruit in fruits:# 每次循环,fruit依次取列表中的值print(f"我喜欢吃{fruit}")print("---")# 注意缩进!这两行都属于循环体print("所有水果都展示完了!")# 这行在循环结束后执行输出:

3. for循环的"能量源":可迭代对象
for循环可以遍历任何"可迭代对象",主要包括:
1. 遍历列表(最常用)
# 计算总分 scores =[85,92,78,90,88] total =0for score in scores: total += score # 累加每个分数print(f"总分:{total}")# 433print(f"平均分:{total /len(scores)}")# 86.62. 遍历字符串
# 统计字符串中的元音字母 text ="Hello, Python!" vowel_count =0for char in text:# 遍历每个字符if char.lower()in'aeiou': vowel_count +=1print(f"元音字母个数:{vowel_count}")# 4 (e, o, o)3. 遍历字典
# 遍历字典有不同的方式 student ={'姓名':'张三','年龄':18,'专业':'计算机'}# 方式1:遍历键for key in student:print(f"键:{key}")# 方式2:遍历值for value in student.values():print(f"值:{value}")# 方式3:同时遍历键和值(最常用!)for key, value in student.items():print(f"{key}:{value}")4. 遍历range()对象
# range(stop): 0到stop-1for i inrange(5):# 0,1,2,3,4print(i)# range(start, stop): start到stop-1 for i inrange(2,6):# 2,3,4,5print(i)# range(start, stop, step): 带步长for i inrange(0,10,2):# 0,2,4,6,8print(i)# 倒序for i inrange(5,0,-1):# 5,4,3,2,1print(i)5. 遍历其他可迭代对象
# 遍历元组 colors =('红','绿','蓝')for color in colors:print(color)# 遍历集合 unique_numbers ={1,2,3,3,2}# {1, 2, 3}for num in unique_numbers:print(num)# 遍历文件行withopen('data.txt','r', encoding='utf-8')asfile:for line infile:# 逐行读取print(line.strip())# 去除换行符4. for循环的进阶技巧
1. enumerate():同时获取索引和值
fruits =['苹果','香蕉','橙子']# 传统方式(不推荐) index =0for fruit in fruits:print(f"第{index}个水果:{fruit}") index +=1# 使用enumerate()(推荐!)for index, fruit inenumerate(fruits):print(f"第{index}个水果:{fruit}")# 从1开始计数for index, fruit inenumerate(fruits, start=1):print(f"第{index}个水果:{fruit}")2. zip():并行遍历多个序列
names =['张三','李四','王五'] scores =[85,92,78] subjects =['数学','语文','英语']# 同时遍历多个列表for name, score inzip(names, scores):print(f"{name}的成绩是{score}分")# 遍历三个列表for name, score, subject inzip(names, scores, subjects):print(f"{name}的{subject}成绩是{score}分")# 注意:zip在最短序列结束时停止 list1 =[1,2,3] list2 =['a','b']for num, char inzip(list1, list2):# 只循环2次print(num, char)3. 嵌套循环:循环中的循环
# 乘法表print("乘法表:")for i inrange(1,4):# 外层循环for j inrange(1,4):# 内层循环print(f"{i} × {j} = {i * j}", end="\t")print()# 换行# 输出:# 乘法表:# 1 × 1 = 1 1 × 2 = 2 1 × 3 = 3 # 2 × 1 = 2 2 × 2 = 4 2 × 3 = 6 # 3 × 1 = 3 3 × 2 = 6 3 × 3 = 94. 循环控制:break、continue、else
# break:提前结束整个循环 numbers =[1,2,3,4,5,6,7,8,9,10]for num in numbers:if num >5:break# 当num>5时,立即结束循环print(num)# 只输出1,2,3,4,5# continue:跳过当前迭代,继续下一次for num inrange(1,11):if num %2==0:# 如果是偶数continue# 跳过本次循环的剩余代码print(num)# 只输出奇数:1,3,5,7,9# else:循环正常结束后执行(没被break中断时)for num inrange(3):print(num)else:print("循环正常结束!")# 会执行for num inrange(3):if num ==1:breakprint(num)else:print("循环正常结束!")# 不会执行,因为被break中断了5. 列表推导式:for循环的简洁写法
# 传统方式:创建平方数列表 squares =[]for i inrange(1,6): squares.append(i **2)# 列表推导式:一行搞定 squares =[i **2for i inrange(1,6)]# 带条件的列表推导式 even_squares =[i **2for i inrange(1,11)if i %2==0]# 字典推导式 squares_dict ={i: i **2for i inrange(1,6)}# 集合推导式 unique_lengths ={len(word)for word in['apple','banana','apple','orange']}5. 动手练习
文本分析器
# 分析一段文本的统计信息 text =""" Python是一种高级编程语言,由Guido van Rossum于1991年创建。 它以简洁、易读的语法而闻名,广泛应用于Web开发、数据科学、人工智能等领域。 """# 请计算:# 1. 总字符数(不含空格和换行)# 2. 总单词数# 3. 每个单词出现的频率# 4. 最长的单词密码生成器
# 生成指定数量的随机密码import random import string defgenerate_passwords(count, length=8): passwords =[]# 你的代码写在这里# 提示:使用嵌套循环# 外层循环生成count个密码# 内层循环生成每个密码的字符return passwords # 测试print(generate_passwords(3,10))while循环
1. 引入:当你不确定需要循环多少次时
想象这样一个场景:你在开发一个猜数字游戏,程序随机生成一个1-100的数字,玩家不断猜测,直到猜中为止。你无法提前知道玩家需要猜多少次——可能1次就中,也可能要猜50次
import random target_number = random.randint(1,100) guess_count =0# 我们不知道循环多少次,只知道继续的条件:没猜中whileTrue:# 看起来像是无限循环,但实际上有break退出 guess =int(input("请猜一个1-100的数字: ")) guess_count +=1if guess < target_number:print("猜小了!")elif guess > target_number:print("猜大了!")else:print(f"恭喜!你猜了{guess_count}次就猜中了!")break# 猜中后退出循环while循环的本质:一种条件控制循环,只要条件为真就不断重复执行。它适用于循环次数不确定,但知道继续条件的场景
2. while循环的基本语法
最简形式
while 条件表达式:# 条件为True时执行的代码块 执行操作()# 通常需要在循环体内改变条件,否则可能无限循环!一个具体例子
# 计数器示例 count =0while count <5:# 条件:count小于5print(f"当前计数: {count}") count +=1# 改变条件变量,这是避免无限循环的关键!# 当count变成5时,条件count<5变为False,循环结束print("循环结束!")输出:

3. while循环的核心要素:条件控制
条件的三种常见形式
1. 计数器控制(知道最大循环次数)
# 模拟加载进度条 progress =0 total =100while progress < total: progress +=10print(f"加载中... {progress}%")# 实际中这里可能会有time.sleep(0.5)模拟延迟print("加载完成!")2. 标志位控制(事件驱动)
# 用户输入验证系统 is_valid =False# 标志位whilenot is_valid:# 当is_valid为False时继续循环 age_input =input("请输入年龄(0-120): ")if age_input.isdigit():# 检查是否为数字 age =int(age_input)if0<= age <=120: is_valid =True# 改变标志位,退出循环print(f"年龄验证通过: {age}")else:print("年龄必须在0-120之间!")else:print("请输入有效的数字!")print("继续处理其他逻辑...")3. 哨兵值控制(特殊值结束)
# 收集学生成绩,直到输入-1结束 scores =[]print("请输入学生成绩(输入-1结束):")whileTrue:# 看起来是无限循环 score_input =input("成绩: ")if score_input =="-1":# 哨兵值break# 遇到哨兵值立即退出if score_input.isdigit(): score =int(score_input) scores.append(score)else:print("请输入数字!")print(f"共收集{len(scores)}个成绩,平均分: {sum(scores)/len(scores):.2f}")4. while循环的高级技巧
1. while-else结构
# else在循环正常结束时执行(非break退出) count =0while count <3:print(f"尝试第{count+1}次")# 这里可以是一些可能失败的操作 count +=1else:print("所有尝试都完成了!")# 与break结合:else不会执行 count =0while count <5:if count ==3:print("提前结束!")breakprint(count) count +=1else:print("这不会执行")# 因为循环被break中断了2. 多个条件的组合
# 复合条件控制 attempts =0 max_attempts =3 success =Falsewhilenot success and attempts < max_attempts: attempts +=1print(f"第{attempts}次尝试...")# 模拟操作,随机成功import random success = random.random()>0.7# 30%成功概率if success:print("操作成功!")elif attempts == max_attempts:print("已达最大尝试次数,操作失败!")3. 在while循环中模拟for循环
# 用while循环实现for循环的功能 fruits =['苹果','香蕉','橙子']# for循环方式for fruit in fruits:print(fruit)# while循环方式 index =0while index <len(fruits):print(fruits[index]) index +=14. 嵌套while循环
# 打印九九乘法表print("九九乘法表:") row =1while row <=9: col =1while col <= row:# 每行列数不超过行数print(f"{col}×{row}={col*row:2d}", end=" ")# :2d表示占2位右对齐 col +=1print()# 换行 row +=15. 无限循环与如何避免
什么是无限循环?
# ❌ 危险的无限循环 count =0while count <5:print(count)# 忘记增加count,条件永远为True!# 程序会一直打印0,直到手动停止# ⚠️ 可能导致无限循环的情况# 1. 忘记更新条件变量# 2. 条件永远为True (while True且没有break)# 3. 逻辑错误导致条件无法变为False如何安全地使用无限循环
# ✅ 安全模式1:明确退出条件whileTrue: user_input =input("输入'quit'退出: ")if user_input.lower()=='quit':break# 明确的退出点print(f"你输入了: {user_input}")# ✅ 安全模式2:添加安全计数器 max_iterations =1000# 安全阀 iteration =0while iteration < max_iterations:# 正常的业务逻辑 iteration +=1# 同时也有正常的退出条件if some_condition:breakif iteration == max_iterations:print("警告:达到最大循环次数!")6. for循环与while循环的区别
| 特性 | for循环 | while循环 |
|---|---|---|
| 循环条件 | 遍历可迭代对象 | 满足条件时继续 |
| 循环次数 | 确定(由序列长度决定) | 不确定 |
| 适用场景 | 遍历已知集合 | 条件满足时重复 |
| 控制方式 | 自动迭代 | 需手动更新条件 |
| 死循环风险 | 较低 | 较高(需谨慎) |
选择指南:
- 知道要循环多少次或遍历什么 → for循环
- 不知道循环次数,但知道继续的条件 → while循环
# for循环:遍历已知集合for student in students: process_student(student)# while循环:条件控制 user_input =""while user_input.lower()!="quit": user_input =input("请输入命令:") process_command(user_input)