2-分支与循环
约 5948 字大约 20 分钟
2026-10-30
第一章学会了变量、数据类型和运算符,已经能写出"输入两个数、输出它们的和"这样的程序。但到目前为止,所有程序都有一个共同特点:从第一行到最后一行,一条道跑到黑,每一行都必然执行、且只执行一次。可现实世界的事务很少这么"直":成绩不及格就要提示补考,密码输错就要拒绝登录——程序得会"看情况办事";全班50个人要挨个算平均分,总不能把同一段代码复制50遍——程序还得会"不厌其烦地重复"。
让程序学会这两样本领的,正是本章的主角——流程控制。任何程序,无论多么庞大,都由三种基本结构搭成:顺序结构(从上到下依次执行)、分支结构(遇到岔路,按条件选一条走)和循环结构(满足条件就走回头路,反复执行)。顺序结构不用学,天生就会;本章要攻克的就是后两种:if分支让程序"会转弯",while和for循环让程序"肯重复"。
本章分两大节:第一节讲分支结构,从最简单的if出发,一路讲到if-elif-else多分支和条件的组合;第二节讲循环结构,先学while再看for,最后打通break、continue和循环嵌套。两道例题都是经典实战:闰年判断、成绩录入统计。学完本章,你写出的程序才算真正"长出了脑筋"。
【学习目标】
知识目标
掌握if、if-else、if-elif-else三种分支结构的语法和执行流程。
掌握比较运算符与and、or、not逻辑运算符,能够书写组合条件。
理解分支嵌套的执行流程,了解三元表达式的写法。
掌握while循环的"计数器三件套"模式和for循环配合range()的用法。
掌握range()的三种写法,理解"含头不含尾"。
掌握break和continue的区别及适用场景。
理解循环嵌套"外层走一步、内层走一圈"的执行流程。
能力目标
能够根据业务规则编写成绩分级、资格判断等分支程序。
能够运用循环完成累加、计数、求最值等典型统计任务。
能够用"哨兵值"控制循环结束,开发录入统计类小程序。
能够运用循环嵌套打印九九乘法表等图形。
素养目标
培养严谨的逻辑思维,养成"先想清楚边界条件再写代码"的习惯。
培养效率意识,体会"让机器做重复的事"这一程序设计的初心。
养成规范缩进的习惯,理解"缩进即归属"这一Python的铁律。
1.分支结构
1.1 为什么需要分支
先看一段"没有判断力"的程序。老师想判断学生的成绩是否及格,写出这样一段代码:
代码 2-1:顺序结构的无奈
score = int(input("请输入成绩:"))
print("及格")
print("不及格")程序运行效果如下(输入85):
请输入成绩:85
及格
不及格成绩明明是85,程序却把"及格"和"不及格"都打印了一遍——因为它不会判断,只会从上到下挨个执行。要让它"看情况办事",就得告诉它:"如果成绩大于等于60,打印及格;否则,打印不及格。"这句"如果……"就是分支结构的灵魂。Python用if关键字来实现它。
1.2 if单分支
最简单的分支:条件成立就做,不成立就什么都不做。
代码 2-2:if单分支
score = int(input("请输入成绩:"))
if score >= 60:
print("及格")
print("继续加油")
print("程序结束")程序运行效果如下(输入85):
请输入成绩:85
及格
继续加油
程序结束再运行一次,输入45,这次只打印"程序结束"——if后面的两行被整体跳过了。if语句的语法要点有三个:
第一,if后面写条件表达式,本例的score >= 60用到了第一章学过的比较运算符(==、!=、>、<、>=、<=),比较的结果只有两种:True或False(布尔类型)。条件为True,才执行分支内的代码。
第二,条件后面必须写冒号,忘写冒号是初学者错误排行榜的第一名。
第三,缩进的行属于if。代码2-2里两个print缩进了4个空格,它们是if的"辖区";最后的print("程序结束")顶格写,不属于if,无论条件真假都会执行。在Python里,缩进不是美观问题,是语法问题——缩进即归属。
还有一个高频笔误要提醒:判断相等要用两个等号score == 60,写成一个等号是赋值,Python会直接报语法错误,帮你把问题拦在运行之前。
1.3 if-else双分支
if单分支只能"满足就做",不满足时无声无息。更多时候我们需要"二选一":满足走这边,不满足走那边——这就是if-else:
代码 2-3:if-else双分支
score = int(input("请输入成绩:"))
if score >= 60:
print("及格")
else:
print("不及格,需要补考")程序运行效果如下(输入45):
请输入成绩:45
不及格,需要补考if-else像一条岔路口:条件为True走if这边的代码块,为False走else那边的代码块,两条路必走其一、只走其一。注意else后面直接跟冒号,不写条件——它本来就是"剩下所有情况"的兜底。
1.4 if-elif-else多分支
成绩判定往往不止两档:90分以上是优秀,80到89是良好,70到79是中等,60到69是及格,60以下是不及格——一条岔路口不够用了,需要一串。这时请出elif(else if的缩写):
代码 2-4:成绩五级判定
score = int(input("请输入成绩:"))
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 70:
print("中等")
elif score >= 60:
print("及格")
else:
print("不及格")程序运行效果如下(输入85):
请输入成绩:85
良好有个细节值得琢磨:为什么elif只写score >= 80,而不用写score < 90 and score >= 80?因为多分支的判断是从上到下依次进行的——程序先查第一个条件score >= 90,85不满足,往下走;查score >= 80时,能走到这里说明第一个条件已经排除了score >= 90的可能,"小于90"是自动成立的。这套规则总结见表2-1。
表 2-1:多分支的判断规则
| 规则 | 说明 |
|---|---|
| 从上到下 | 依次检查每个条件,一个一个来 |
| 命中即止 | 某个条件为True,执行它的代码块后,整个if-elif结构立即结束 |
| 条件自动排除 | 走到后面的elif,说明前面的条件都不成立,可以少写条件 |
| else兜底 | 所有条件都不满足时执行,最多只能有一个,且必须放在最后 |
特别提醒一个"顺序陷阱":如果把score >= 60这一档写到最前面,输入95时程序会只判到"及格"就收工——因为95 >= 60成立,命中即止,后面的条件根本没机会检查。所以写多分支时,条件要按"从苛刻到宽松"(或区间互斥)的顺序排布,让最严格的条件先把关。这套五级判定逻辑请务必写熟,第三章的综合例题会直接用到。
1.5 条件的组合与嵌套
单个条件不够用时,可以用逻辑运算符把多个条件串起来。Python有三个逻辑运算符:and(并且)、or(或者)、not(取反),规则见表2-2。
表 2-2:逻辑运算符
| 运算符 | 含义 | 判断规则 | 示例 |
|---|---|---|---|
| and | 并且 | 两边都为True,结果才是True | score >= 60 and score <= 100 |
| or | 或者 | 两边只要有一个为True,结果就是True | age < 12 or age >= 65 |
| not | 取反 | True变False,False变True | not password == "123456" |
代码 2-5:组合条件
# 考核通过:笔试和面试都必须及格——用and
written = 78
interview = 85
if written >= 60 and interview >= 60:
print("两门都及格,考核通过")
# 符合免考:年龄满35岁,或者持有高级职称——用or
age = 30
title = "高级工程师"
if age >= 35 or title == "高级工程师":
print("符合免考条件")
# 未登录就提示——用not
logged_in = False
if not logged_in:
print("请先登录")程序运行效果如下:
两门都及格,考核通过
符合免考条件
请先登录记忆窍门:and是"苛刻的",全对才算对;or是"宽容的",对一个就行;not是"反着说的"。当and、or混在一个条件里时,建议像数学式子一样用圆括号标清楚分组,如(score >= 60 and score <= 100) or score == 999,既不容易错,读起来也清楚。
分支里面还可以再放分支,这叫嵌套。比如先检查成绩是否合法(0~100),合法时再判断及格与否:
代码 2-6:分支嵌套与"拉平"改写
# 写法一:嵌套(下面两种写法二选一使用,放在一起只为对照)
score = int(input("请输入成绩(0~100):"))
if score >= 0 and score <= 100:
if score >= 60:
print("及格")
else:
print("不及格")
else:
print("成绩无效")
# 写法二:拉平——用elif把层级展开,效果完全相同
if score < 0 or score > 100:
print("成绩无效")
elif score >= 60:
print("及格")
else:
print("不及格")两种写法结果一模一样,但写法二只有一层,读起来轻松得多。经验法则:嵌套超过两层,人就很难一眼看懂逻辑了,能拉平就拉平——把"不合法"这种特殊情况提到最前面先处理(行话叫"卫语句"),主干逻辑就能保持清爽。
最后带一个"了解即可"的写法:三元表达式。它把简单的if-else压缩成一行,适合"二选一取值"的场合:
代码 2-7:三元表达式(了解即可)
score = 85
result = "及格" if score >= 60 else "不及格"
print(result) # 输出:及格读法:"及格" if 条件 else "不及格"——条件成立取前面的值,不成立取后面的值。它只是if-else的简写,不影响功能,看懂别人的代码即可,初学阶段老老实实用完整写法更清晰。
【例题 2-1】闰年判断
闰年的判定规则是:能被4整除但不能被100整除,或者能被400整除。请编写程序,输入一个年份,判断它是否为闰年。
分析:这道题是"组合条件"的经典应用。"能被4整除"用第一章学过的取余运算符表示为year % 4 == 0;整句话翻译成Python就是:(year % 4 == 0 and year % 100 != 0) or year % 400 == 0。
代码 2-8:闰年判断程序
year = int(input("请输入年份:"))
if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
print(str(year) + "年是闰年")
else:
print(str(year) + "年不是闰年")程序运行效果如下(分别运行三次):
请输入年份:2024
2024年是闰年
请输入年份:1900
1900年不是闰年
请输入年份:2000
2000年是闰年三个测试用例正好覆盖了规则的三种典型情况:2024年能被4整除且不被100整除,是闰年;1900年虽然能被4整除,但也能被100整除,被第一关拦下,又没到400的倍数,不是闰年;2000年是400的倍数,走第二关通过,是闰年。写这类程序时,最有价值的习惯就是像这样挑选"卡边界"的数据来验证——只测2024这种顺手年份,是测不出Bug的。
2.循环结构
2.1 为什么需要循环
新学期开学,班主任要求程序打印100遍"好好学习"。按目前所学只能这么写:
代码 2-9:复制粘贴式打印
print("第1遍:好好学习")
print("第2遍:好好学习")
print("第3遍:好好学习")
print("第4遍:好好学习")
print("第5遍:好好学习")
# ……接着抄第6遍、第7遍,一直抄到第100遍五遍还能忍,一百遍呢?而且仔细观察会发现:这100行代码几乎长得一模一样,只有那个编号在变。规律如此明显的事情,理应交给程序自己完成——"一段代码重复执行、每次只有个别数据在变",这正是循环结构要解决的问题。Python提供了两种循环:while和for。
2.2 while循环
while的意思是"当……的时候":只要条件成立,就反复执行循环体。先用它重写打印任务:
代码 2-10:while打印5遍
count = 1 # ① 计数器初始化
while count <= 5: # ② 循环条件
print("第" + str(count) + "遍:好好学习")
count += 1 # ③ 计数器更新,千万别忘
print("打印完毕")程序运行效果如下:
第1遍:好好学习
第2遍:好好学习
第3遍:好好学习
第4遍:好好学习
第5遍:好好学习
打印完毕这段代码里的三件套是while循环的标准骨架:①初始化计数器(count = 1);②写循环条件(count <= 5);③在循环体内更新计数器(count += 1,它是count = count + 1的简写,加号、等号连写)。程序每执行一轮循环,就回头检查一次条件:count是1、2、3、4、5时条件都成立,继续循环;count变成6时条件不成立,退出循环,接着执行while后面的print("打印完毕")。
③千万不能忘。如果把count += 1删掉,count永远是1,条件永远成立,程序会无限打印下去——这叫死循环。真遇上了,在命令行窗口按Ctrl+C可以强行终止程序。
while更拿手的场景其实是"次数事先不确定"的任务,比如经典累加问题"计算1+2+3+…+100":
代码 2-11:用while计算1加到100
total = 0 # 累加器:先把和初始化为0
i = 1 # 计数器从1开始
while i <= 100:
total += i # 把i累加进总和
i += 1 # i自己也要往前走
print("1加到100的和是" + str(total))程序运行效果如下:
1加到100的和是5050"累加器"是比"计数器"更通用的思想:total += i表示"在原有基础上加一点"。循环开始前累加器要清零,循环体内一边积累一边推进——这个模式在本章例题2-2中还会反复出现,务必吃透。
2.3 for循环与range()
再看打印任务,"重复5次"这种次数明确的活,Python准备了更省事的写法——for循环:
代码 2-12:for打印5遍
for i in range(1, 6):
print("第" + str(i) + "遍:好好学习")
print("打印完毕")运行效果与代码2-10完全相同,但代码短了一半:初始化和更新计数器的活,for+range()全包了。for i in range(1, 6)读作"让i依次取1、2、3、4、5,每取一个值,执行一遍循环体"。i能取哪些值,全由range()决定,它的三种写法见表2-3。
表 2-3:range()的三种写法
| 写法 | 生成的数 | 记忆要点 |
|---|---|---|
| range(5) | 0、1、2、3、4 | 只给一个数时,从0开始,不到5 |
| range(1, 6) | 1、2、3、4、5 | 含头不含尾:包含1,不包含6 |
| range(0, 10, 2) | 0、2、4、6、8 | 第三个参数是步长,每次跳2格 |
| range(5, 0, -1) | 5、4、3、2、1 | 步长为负数,倒着数 |
"含头不含尾"是range()最重要的性格:range(1, 6)生成的最大数是5不是6。这样设计有个好处——range(1, 6)正好生成5个数,起点和终点一减就知道个数,配合循环"重复n次"时写range(n)即可(从0数到n-1,正好n次)。
代码 2-13:range()三种写法体验
# 写法一:range(终点)——从0开始
for i in range(5):
print(i, end=" ")
print()
# 写法二:range(起点, 终点)——含头不含尾
for i in range(1, 6):
print(i, end=" ")
print()
# 写法三:range(起点, 终点, 步长)
for i in range(0, 10, 2):
print(i, end=" ")
print()
# 步长为负,倒数
for i in range(5, 0, -1):
print(i, end=" ")
print()程序运行效果如下:
0 1 2 3 4
1 2 3 4 5
0 2 4 6 8
5 4 3 2 1小提示:print默认打印完会换行,这里用end=" "把换行换成了空格,只为让数字排成一行,看起来紧凑;不习惯的话,把end=" "去掉、每个数单独占一行也完全可以。
那while和for怎么选?记一句话:次数已知用for,条件驱动用while。"打印5遍""算1到100"这种活,for一步到位;"用户一直输入,直到输对为止""读到文件末尾为止"这种事先不知道几轮的活,交给while。第三章遍历字符串和列表时,for还会再度进化,成为出镜率最高的循环。
2.4 break与continue
循环有时需要"提前离场"或"跳过某几轮",这就要用到两个关键字:break和continue。
break用来立即终止整个循环:
代码 2-14:break——找到就收工
# 找出第一个能被7整除的三位数
for n in range(100, 1000):
if n % 7 == 0:
print("第一个能被7整除的三位数是" + str(n))
break # 找到就收工,后面800多次判断全省了程序运行效果如下:
第一个能被7整除的三位数是105没有break,循环会老老实实从100查到999,就算105已经找到,后面每一轮还得白跑。break像"提前下班":一执行,整个循环立刻结束,程序跳到循环之后继续执行。
continue则温和得多,只跳过当前这一轮:
代码 2-15:continue——跳过这一轮
# 打印1~10中的所有奇数
for i in range(1, 11):
if i % 2 == 0:
continue # 是偶数就跳过本轮,回到循环取下一个数
print(i)程序运行效果如下:
1
3
5
7
9i是偶数时,continue把它"赶回"循环开头,本轮后面的print(i)不执行,直接进入下一轮。打个比方:break是"这单不干了,下班";continue是"这单跳过,接着干下一单"。初学者最容易把两者用反——想让循环"少做几轮"却写成break,结果后面的数据全没处理。写之前问自己一句:是"不干了"还是"这单跳过"?
2.5 循环嵌套
循环体里还可以再放循环,这叫循环嵌套。外层循环每走一步,内层循环要完整地跑完一圈——这句是理解嵌套的钥匙。先看打印三行星号的例子:
代码 2-16:打印矩形星号
for i in range(3): # 外层循环:管行,共3行
for j in range(5): # 内层循环:管列,每行连续打印5个星号
print("*", end="")
print() # 一行星号打完,换行程序运行效果如下:
*****
*****
*****执行过程拆解:外层i=0时,内层j从头到尾跑一圈,打印5个星号;然后print()换行;外层i=1,内层再跑一圈……外层一共走3步,每步内层走5步,总共打印15个星号。内层的end=""让5个星号挤在同一行,print()负责换行——一个管横排、一个管竖排,分工明确。
把这套"外层管行、内层管列"的思路套到乘法上,就是编程入门的经典题:九九乘法表。
代码 2-17:九九乘法表
for i in range(1, 10): # i:第几行,从第1行到第9行
for j in range(1, i + 1): # j:第i行有i个算式,从1数到i
print(str(j) + "×" + str(i) + "=" + str(j * i), end="\t")
print() # 一行算式打完,换行程序运行效果如下:
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81最妙的一行是range(1, i + 1):第1行只打1个算式,第2行打2个……内层的终点跟着外层走,这正是嵌套循环的精髓——内层的节奏由外层控制。end="\t"里的\t是制表符,让同一行的算式按列对齐。循环嵌套的层数别贪多,两层足够应对绝大多数场景;层数一深,执行次数是成倍膨胀的(两层循环各100次就是1万次组合),脑子转不过来,机器也可能被拖慢。
【例题 2-2】成绩录入与统计小程序
请编写一个成绩统计程序:用户可以不断录入成绩(0~100的整数),输入负数表示录入结束;录入结束后,程序输出录入人次、平均分、最高分和及格人数。
分析:这道题是全章知识的会师战。录入"多少个成绩事先不知道",用while配合"哨兵值"(负数)控制结束;统计需要累加器(总分)、计数器(人次、及格人数)和"打擂台"(求最高分);每一轮录入后还要用分支判断是否及格。
代码 2-18:成绩录入与统计小程序
total = 0 # 累加器:总分
count = 0 # 计数器:人次
max_score = -1 # 最高分:先放一个不可能更小的值
pass_count = 0 # 计数器:及格人数
score = int(input("请输入成绩(负数结束):"))
while score >= 0:
total += score # 累加总分
count += 1 # 人次加一
if score > max_score: # 打擂台:新来的比擂主大,就换擂主
max_score = score
if score >= 60: # 及格了就计数
pass_count += 1
score = int(input("请输入成绩(负数结束):"))
print("-------------------------")
if count > 0:
print("共录入" + str(count) + "人")
print("平均分:" + str(total / count))
print("最高分:" + str(max_score))
print("及格人数:" + str(pass_count) + "人")
else:
print("没有录入任何成绩")程序运行效果如下(输入部分用【】标注):
请输入成绩(负数结束):【85】
请输入成绩(负数结束):【92】
请输入成绩(负数结束):【45】
请输入成绩(负数结束):【78】
请输入成绩(负数结束):【-1】
-------------------------
共录入4人
平均分:75.0
最高分:92
及格人数:3人这段程序有几个值得回味的细节。第一,"哨兵值"模式:负数本身不是成绩,它只是一个暗号,告诉循环"到此为止"——录入类的程序几乎都用这招。第二,求最高分的"打擂台"思路:先立一个擂主max_score = -1,每个新成绩上来挑战,赢了就上位。第三,最后的if count > 0防御:万一用户一开始就输负数,total / count会变成除以零错误,先检查人次再输出,程序才够健壮。
不过,这道题也留了个明显的"憋屈":成绩录进去,统计完就没了——程序里根本没有地方把85、92、45、78这批成绩保存下来,想按成绩排序、想列出不及格的名单、想把平均分保留一位小数,都无从下手。原因只有一个:到现在为止,我们手里只有一个个独立的变量,还没有一种能"把一批数据装在一起"的容器。这个容器,就是下一章的主角——字符串与列表。
