第 11 章 判断、循环和数据容器
文章目录
- 第 11 章 判断、循环和数据容器
- 🌈 关于《AI 零基础 36 讲》
- 让程序根据条件选择动作
- 用列表保存一组有顺序的项目
- for 把同一段动作交给每一项
- while 持续检查条件
- 元组、集合与字典各自保存什么
- 先检查为空,再决定怎样访问
- 把学习记录真正统计起来
- 报错时先找出哪一种访问失败
- 资料来源
上一章的学习时间计算器一次处理一份输入。把三天的记录交给它,你还得手动运行三次。想让程序逐条检查、统计完成数量,还需要能保存多条记录的容器,以及决定怎样处理每条记录的判断和循环。
这一章会做出一个学习记录统计程序。它能数出已完成项目,列出未完成标题,累加学习分钟数,也会告诉你哪些记录缺了字段。你需要先完成第 9 章的环境安装,并能读懂第 10 章的变量、字符串和类型转换。全部示例使用标准库,不需要安装新包。
正文中的短代码可分别保存成文件运行,完整演示另见 container_demo.py。
🌈 关于《AI 零基础 36 讲》
🧠零基础也能开始学 AI
《AI 零基础 36 讲》是一套面向初学者的 AI 入门教程。课程从认识和使用 AI 开始,逐步学习
Python、数据处理、机器学习与深度学习,再进入大模型应用开发,最后完成个人知识库助手的综合实践。📖本篇是正式讲解文章
通过具体例子、原创图示和操作结果讲清概念。涉及编程的章节提供可运行代码,方便你边读边试。
🎯读完以后,动手检验
建议按专栏顺序学习,并完成对应的课后训练,检查自己能否独立运用本章知识。
博客专栏:《AI 零基础 36 讲》
让程序根据条件选择动作
先设一个很小的目标,每天学满 30 分钟就提示达标。
minutes=35ifminutes>=30:print("今天达标")else:print("再学一会儿")print(minutes>=30)结果是“今天达标”和 True。把第一行改成 20,结果会变成“再学一会儿”和 False。minutes >= 30得到的是布尔值,也就是表示真假的 True 或 False。
if后面放条件。条件为真时,执行它下面缩进的语句;条件为假时,这段跳过。else给出另一种动作。冒号和缩进都是 Python 语法的一部分,下一层统一缩进四个空格。不要把全角冒号输入代码,也不要混用 Tab 与空格。
判断相等用==,赋值用=。不相等写!=。>和<分别比较大小,>=与<=允许两边相等。需要三个分支时,可以加elif,意思是前面的条件不成立,再检查这一项。
minutes=0ifminutes==0:print("今天还没开始")elifminutes<30:print("已经开始,继续学习")else:print("今天达标")这个程序输出“今天还没开始”。一个 if、elif、else 结构最多执行其中一个分支,命中前面的条件后就跳过剩余分支。把分钟数依次改成 15 和 30,可以分别看到另外两种结果。
条件还能组合。and要求两边都成立,or只需有一边成立,not把当前真假反过来。
minutes=35print(minutes>=30andminutes<60)print(minutes==0orminutes>=30)print(notFalse)三行都输出 True。在这组数字下,35 满足两个范围限制,也满足第二行的后一个条件。先分别读懂两边,再读组合起来的条件,通常比一次写很长的判断更容易排错。
用列表保存一组有顺序的项目
列表 list 是按顺序保存多个元素的容器,用方括号表示。元素就是其中的一项,可以是字符串、数字,也可以是别的容器。
topics=["变量","循环","列表","字典"]print(topics[0])print(topics[-1])print(topics[1:3])print(topics[10:])输出依次如下。
变量 字典 ['循环', '列表'] []下标是元素的位置编号,从 0 开始,所以第一项是 topics[0]。负下标从末尾数,-1 指最后一项。列表有四项,正下标只有 0 到 3。直接访问 topics[4] 会产生 IndexError,表示位置越界。
topics[1:3]是切片,取出从位置 1 开始,到位置 3 之前的部分。右端不包含在结果里。省去左端就从开头取,省去右端就取到末尾。切片范围超出列表长度时会截到边界,所以topics[10:]得到空列表。单项下标越界和切片超出范围有不同的处理方式。
列表内容可以修改。append在末尾加入一项,也可以通过下标替换原来的元素。
topics=["变量","循环"]topics.append("列表")topics[0]="变量复习"print(topics)结果是['变量复习', '循环', '列表']。把新内容写到 topics[0] 会替换第一项,原来的“变量”就不再占这个位置。
for 把同一段动作交给每一项
遍历是按顺序取出容器里的元素。for循环很适合这种“对现有的一组东西逐条处理”的任务。
topics=["变量","循环","列表"]fortopicintopics:print("准备复习",topic)结果有三行,分别是“准备复习 变量”“准备复习 循环”和“准备复习 列表”。每轮开始,topic 都会指向当前元素。你给它取别的变量名也可以,名字要帮助自己记住取出来的是什么。
统计数字时,先在循环外准备一个累计值。
total=0forminutesin[20,15,25]:total+=minutesprint(total)+=把右边的数字加到当前值上,等价于total = total + minutes。三轮以后 total 依次变成 20、35、60,最后打印 60。若把 total = 0 缩进到循环内部,每一轮都会重置累计值,结果就只剩 25。这是统计程序很常见的错误。
想同时取到编号和内容,可以用enumerate。它在遍历时附上计数,start=1 让展示编号从 1 开始;列表自身的下标仍然从 0 开始。
fornumber,topicinenumerate(["变量","循环"],start=1):print(number,topic)fornumberinrange(1,4):print("复习轮次",number)前两行输出“1 变量”和“2 循环”,后面显示轮次 1、2、3。range(1, 4) 提供从 1 到 4 之前的整数,右端同样不包含在内。先画出自己需要的数字,再写 range 的边界,可以少一次来回修改。
while 持续检查条件
while适合“只要条件还成立,就继续做”的任务。次数可能事先不知道,比如持续接收输入直到用户要求退出。下面先使用一个有明确终点的例子。
remaining=3whileremaining>0:print("剩余次数",remaining)remaining-=1print("停止")它依次显示剩余次数 3、2、1,最后显示“停止”。每一轮都执行 remaining -= 1,条件随后会变假。如果漏掉减一,remaining 一直是 3,程序就会不停重复。在终端可以按 Ctrl+C 中断,再补上改变条件的语句。
break可以立即离开所在循环,continue会跳过本轮剩余动作,进入下一轮。统计坏记录时,我们会用 continue 跳过它,继续检查下一条。第一次练习时优先用 for 处理现成列表;需要反复检查退出条件,再考虑 while。
元组、集合与字典各自保存什么
元组 tuple 也有顺序,常用圆括号表示。它的元素位置不能重新赋值,适合保存一组本章不打算修改的值,比如两个坐标。
point=(2,5)x,y=pointprint(x,y)print(point[0])print(("Python",))输出是“2 5”、2 和('Python',)。把两个元素分别交给 x、y 叫解包,左边名字数量要与右边元素数量对应。单元素元组需要保留逗号。元组若包含列表,那个列表仍然可以修改,元组限制的是它自身各位置的赋值。
集合 set 保存不重复的元素,适合去掉重复标签,或检查某个标签是否存在。它不承诺按加入顺序显示,也没有列表那样的位置下标。
tags={"Python","AI","Python"}print(len(tags))print("AI"intags)print(len(set()))输出是 2、True 和 0。重复的 Python 只保留一个。in检查成员,len取数量。空集合写 set();空花括号{}表示空字典。示例只打印数量和成员检查结果,避免把集合显示顺序当作固定答案。
字典 dict 把名字和对应的值放在一起。名字叫键 key,对应内容叫值 value。这正适合描述一条学习记录。
record={"title":"列表","done":False,"minutes":25}print(record["title"])print(record.get("note","暂无备注"))forkey,valueinrecord.items():print(key,value)前两行输出“列表”和“暂无备注”,随后输出 title、done、minutes 三组键值。items()让遍历同时得到键和值。只写for key in record会取到键。直接取不存在的键会产生 KeyError,get则允许给出缺省值。
该用哪一种,取决于处理动作。要保留学习项目顺序并随时追加,用列表;一条项目有多个命名字段,用字典。去重标签交给集合,位置固定的一组值可以用元组。本章最终数据就是一个列表,里面的每个元素是一条字典记录。
先检查为空,再决定怎样访问
空列表、空字典、空集合参加条件判断时都会按假处理,非空容器通常按真处理。这让“有没有内容”的检查可以写得很短。
topics=[]iftopics:print(topics[0])else:print("还没有学习项目")print(bool([False]))第一部分显示“还没有学习项目”,最后一行是 True。列表 [False] 里面有一个元素,容器就非空;这个判断没有自动检查里面的完成状态。要检查每一项是否完成,仍然需要遍历并读取 done 字段。
列表赋值也有一个容易漏看的地方。
original=["变量","循环"]alias=original alias.append("列表")print(original)copied=original.copy()copied.append("字典")print(original)print(copied)前两次打印 original 都是['变量', '循环', '列表'],copied 最后多出“字典”。alias 和 original 指向同一个列表,copy 创建了另一个列表。本例只有字符串元素;列表若嵌套字典,普通 copy 仍会共享内部字典,修改内部字段时要另外考虑复制范围。
把学习记录真正统计起来
打开 study_records.py。文件开头已经放好六条教学记录,前三条字段完整,第四条缺 done,第五条把 False 写成了字符串,最后一条连字典都没有使用。这样的输入让你能同时看到正常统计和错误说明。
程序先检查外层是不是列表,再逐条检查字典和必需字段。title 必须是非空字符串,done 必须是真正的布尔值,minutes 必须是非负整数。isinstance(value, list)可以判断值是不是列表;本章对 done 和 minutes 使用type(value) is bool与type(value) is int,严格限制输入类型。Python 中 bool 属于 int 的子类,直接用 isinstance 检查分钟数会接纳 True,我们在这里明确排除它。
为什么不能看到“False”就当成未完成?bool("False")得到 True,因为非空字符串参加真假判断时为真。完成状态必须保留没有引号的 True 或 False。缺失状态也不能悄悄归入未完成,程序会把它列为无效记录,等待补充。
运行下面的命令。执行前先在终端进入本章目录,可以在编辑器中打开本章文件夹后再新建终端。
python examples/study_records.py实际结果如下。
输入 6 条,有效 3 条,无效 3 条 已完成 2 项 未完成 ['循环'] 有效记录共 60 分钟 第 4 条缺少字段 第 5 条 done 必须是布尔值 第 6 条必须是字典60 来自 20 + 15 + 25,包括有效但尚未完成的那条学习记录。已完成数量只数 done 为 True 的条目。两个统计口径分别回答“有效记录一共花了多少时间”和“已经完成多少项目”,不能混在一起。
把 records 改成[]再运行,会得到 0 条记录、0 项完成、空的未完成列表和 0 分钟。for 没有元素可以取,就直接结束,所以累计值必须在循环前设好。把 records 改成数字 3,会得到“输入错误,请提供列表”,统计过程停止。
报错时先找出哪一种访问失败
本章保存了三组可以直接运行的错误与修复文件。空列表取第一项会触发 IndexError,修复版先检查列表是否有元素,空时显示“还没有学习项目”。字典缺 done 会触发 KeyError,修复版先检查字段,显示“缺少完成状态,请补充记录”。把整数交给 for 会触发 TypeError,修复版先检查外层类型,显示“输入错误,请提供列表”。
- 【零基础学AI】第 11 章课后练习与答案
下一章会把这段统计过程整理成函数,并让程序从文件读取文本、把结果保存为 JSON。
资料来源
- 【零基础学AI】第 11 章课后练习与答案
- Python 条件判断和循环
- Python 列表、元组、集合和字典
- Python 真假判断和内置类型