AI 技术博客
返回首页
Python 基础 20 分钟阅读

第三章综合测验:分支与循环

Python综合测验流程控制

引言

这是第三章「流程控制」的最后一篇,收卷篇。前九篇的递进路线是:分支(第 1 篇 if/elif/else、第 2 篇 match)→ 循环(第 3 篇 while、第 4 篇 for 与 range、第 5 篇 break/continue/else)→ 组合(第 6 篇嵌套、第 7 篇推导式)→ 实战(第 8 篇 FizzBuzz 与猜数字、第 9 篇图案打印与交互式菜单)。本篇做四件事:十道选择题把每个知识点的「精确版本」逼出原形;三道编程题给出可运行的参考解答;一个综合任务(成绩统计系统)要求把分数段分支、重复录入循环与统计汇总写进同一个程序;最后一份学习自检清单供你逐项画勾。测验范围严格限于前九篇;函数(def)、异常处理(try/except)与类分别属于第四、七、八章,这里只在参考解答的旁注里以预告形式出现,不作为考点。作答方式与第二章相同:先闭卷写答案与理由,再对照解析,理由比答案重要——答对但理由写错,说明对应篇目需要回读。

测验说明

十题均为单选,每题标注出处与考点,难度从记忆级到综合级递增。代码片段都已在本机(CPython 3.12)实测,输出真实可复现。建议每题限时两分钟。

第 1 题(出处:第 1 篇 分支结构)

n = 75
if n >= 90:
    grade = "A"
elif n >= 60:
    grade = "C"
elif n >= 70:
    grade = "B"
print(grade)

输出是?(A)"A" (B)"B" (C)"C" (D)报错

第 2 题(出处:第 2 篇 模式匹配)

x = 9
match x:
    case 1 | 2:
        r = "小"
    case _ if x > 5:
        r = "大"
    case _:
        r = "中"
print(r)

输出是?(A)"小" (B)"大" (C)"中" (D)报错

第 3 题(出处:第 3 篇 while 循环)

i = 0
total = 0
while i < 10:
    i += 1
    if i % 3 == 0:
        continue
    total += i
print(total)

输出是?(A)37 (B)40 (C)45 (D)55

第 4 题(出处:第 4 篇 for 与 range)

print(list(range(2, 11, 3)))

输出是?(A)[2, 5, 8] (B)[2, 5, 8, 11] (C)[3, 5, 8] (D)报错

第 5 题(出处:第 5 篇 循环控制三件套)

n = 7
for d in range(2, n):
    if n % d == 0:
        print("合数")
        break
else:
    print("质数")

输出是?(A)"合数" (B)"质数" (C)什么都没有输出 (D)报错

第 6 题(出处:第 6 篇 嵌套与组合)

for i in range(1, 4):
    for j in range(1, 4):
        if j == 2:
            break
        print(i, j)

程序打印出的行是?(A)三行:1 1、2 1、3 1 (B)三行:1 1、1 2、1 3 (C)六行:1 1、1 2、2 1、2 2、3 1、3 2 (D)一行:1 1

第 7 题(出处:第 7 篇 推导式)

print({x * x for x in range(1, 4)})

输出是?(A){1, 4, 9} (B)[1, 4, 9] (C){1, 4, 9, 16} (D)报错

第 8 题(出处:第 8 篇 实战:猜数字的循环骨架)

n, c = 0, 0
while True:
    n += 2
    c += 1
    if n > 10:
        break
print(c)

输出是?(A)4 (B)5 (C)6 (D)7

第 9 题(出处:第 9 篇 实战:图案打印)

for i in range(1, 4):
    print("*" * i)

输出是?(A)三行星号,个数依次 1、2、3 (B)三行星号,个数依次 3、2、1 (C)只有一行 3 个星号 (D)报错

第 10 题(出处:综合,第 7 篇生成器表达式)

print(sum(x for x in range(1, 5) if x % 2 == 0))

输出是?(A)6 (B)4 (C)10 (D)报错,生成器表达式不能参与求和

计分与回读表

每题 10 分。先按下表判断分数段:8 分以下,把错题对应的篇目重读一遍再返场;9~10 分,直接挑战后面的编程题。分数只说明哪些点没建起来,「回读」才是补齐动作——错题对应篇目一览:

题号考点回读篇目
1elif 链从上到下首个命中即结束第 1 篇「分支的执行流程」小节
2match 的或模式、守卫(guard)与 _ 兜底第 2 篇「结构化模式」小节
3continue 跳过本轮剩余语句第 3 篇 + 第 5 篇「continue」小节
4range 三参数:含头不含尾、步长第 4 篇「range 的三种形态」
5for-else:未 break 结束时执行 else第 5 篇「else 子句」小节
6break 只终止最近一层循环第 6 篇「嵌套与作用域」
7集合推导式与序列推导式的区别第 7 篇「字典与集合推导式」
8while True 必须配合显式 break第 8 篇「猜数字的循环骨架」
9字符串乘法与行号驱动输出第 9 篇「直角三角形」小节
10生成器表达式按需产出、sum 逐个累加第 7 篇「生成器表达式」小节

解析:每题为什么

第 1 题:C。 elif 链从上到下检查,命中第一个为真的分支后整条链结束,后面不再看。n = 75 先命中 n >= 60 的 "C" 分支,位于其后的 n >= 70 分支永远轮不到——这正是第 1 篇强调的「先宽后窄陷阱」:区间倒序(90 以上 → 60 以上 → 70 以上)会让中间档位永久不可达。若把条件改为 70 <= n < 90 这类窄区间并用倒序排列,三个分支才各得其所。

第 2 题:B。 match 自上而下尝试各 case:先试或模式(OR pattern)1 | 2,9 不匹配;再试带守卫(guard)的 case _ if x > 5——守卫是附加在模式上的布尔条件,_ 匹配任何值但守卫 9 > 5 为真才进入,于是 r = "大";case _ 兜底分支根本没机会执行。容易错在把守护条件当成 case 前的 if,或忘了「守卫为假时继续往下试」的规则。

第 3 题:A。 循环把 1 到 10 逐个加进 total,i % 3 == 0 时 continue 跳过本轮剩余的 total += i,因此 3、6、9 三个数被剔除:1 到 10 的总和 55 减去 18(3+6+9)得 37。continue 在 while 里的语义与 for 一致:跳过本轮剩余语句,直接回到条件判断(for 则是取下一个元素)。

第 4 题:A。 range(2, 11, 3) 从 2 开始、每次加 3、不含 11:2、5、8。range 的「含头不含尾」与负步长(range(10, 3, -2) 得 10、8、6、4)是第 4 篇的两个核心边界,步长与停止临界点差一即错。list() 只是把 range 对象显形,不是考点本身。

第 5 题:B。 for-else 的 else 在循环没有被 break 打断、正常耗尽时执行。7 在 2 到 6 之间没有因子,内层循环完整走完,else 输出 "质数"。这是第 5 篇「循环后收尾」的经典应用:把「是否找到」的布尔旗标省掉,语义直接写在结构里。若 n 换成 9,找到因子 d = 3 后 break,else 不执行。

第 6 题:A。 内层循环在 j == 2 时 break,break 只终止最近一层循环,外层不受影响继续推进 i。于是每一行 i 都只打印 j = 1 一项,共三行。对 C 语言的 break 经验尤其要警惕:Python 没有「标注跳出哪一层」的语法,逃出多层嵌套只能靠旗标变量或结构重排,这正是第 6 篇嵌套主题的易错核心。

第 7 题:A。 {x * x for x in range(1, 4)} 是集合推导式(set comprehension),产出集合 {1, 4, 9}。集合无序,但打印内容确定且与 [1, 4, 9] 的字面顺序一致(CPython 对小型整数集合保持插入序);集合还会自动去重。选项 B 是列表推导式的产物,类型错误;选项 C 的 16 是 range(1, 4) 到达不到 x = 4 的幻觉项——range 含头不含尾再次作案。

第 8 题:C。 while True 没有退出条件,必须靠循环体内的 break 主动终止。每轮 n 加 2:2、4、6、8、10、12,n > 10 时 break,共 6 轮。第 8 篇猜数字把这一骨架用在「猜中才退出」上;本题提醒:while True 本身没有错,错误永远是「忘写或错写 break」——写前先问自己「哪条路径退出」。

第 9 题:A。 字符串乘法 "*" * i 产生 i 个星号,for i in range(1, 4) 让 i 依次取 1、2、3,输出三行递增星号。这是第 9 篇图案打印的最小单元:行号驱动行内容(行-列建模的退化形式)。常见错因是把 range(1, 4) 误记成 range(4) 或忘记含头不含尾——本題的 i 永远取不到 4。

第 10 题:A。 生成器表达式(generator expression)(x for x in range(1, 5) if x % 2 == 0) 按需产出 2 与 4,sum 逐个累加得 6。它与列表推导式的差别在第 7 篇讲过:推导式立刻建出整个列表,生成器表达式惰性产出、一次只给一个值,适合喂给 sum、len 这类「消费完就丢」的目标。选项 D 是典型幻觉——生成器完全可以求和,只是不能索引、不能重复遍历。

三道易错共性再强调一遍:一是差一(range 的含头不含尾、n - 1 的边界);二是作用域(break 只跳出本层、continue 只跳过本轮);三是类型(input 的字符串、集合与列表的分野)。这三条正是第三章全部易错点的收敛。

编程题:三道与参考解答

三道题按难度递进,全部只用前九篇的知识即可完成;参考解答同样未使用函数与异常处理,以便你对照自己的写法。

第 1 题(嵌套 + break + else):打印 2~100 的全部质数。 用双层循环:外层遍历候选数 n,内层试探因子;找到因子立即 break,循环耗尽未 break 则 n 是质数。

for n in range(2, 101):
    for d in range(2, n):
        if n % d == 0:
            break
    else:
        print(n, end=" ")
print()
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

内层 for-else 直接表达「没找到因子」;print(n, end=" ") 让质数在同一行输出——end 参数控制 print 的行尾字符,默认是换行。可以顺手做个复杂度分析:内层最坏试探到 n/2,整体量级约 O(n²) 次取余,对 100 以内的规模完全够用;后续课程会学到更快的筛法。

第 2 题(while True + break + 输入守卫):循环录入整数求和。 反复读取整数并累加,输入 -1 结束;非数字输入给出提示、不计入统计;结束后报告有效个数与总和。

total = 0
count = 0
while True:
    s = input("输入整数(-1 结束):").strip()
    if s == "-1":
        break
    if not s.isdigit():
        print("无效输入,请重新输入")
        continue
    total += int(s)
    count += 1
print(f"共 {count} 个有效数,总和 {total}")
输入整数(-1 结束):5
输入整数(-1 结束):0
输入整数(-1 结束):12
输入整数(-1 结束):abc
无效输入,请重新输入
输入整数(-1 结束):-1
共 3 个有效数,总和 17

一个真实的坑:"-1" 含负号,"-1".isdigit() 是 False,所以退出条件必须先于 isdigit 判断,否则永远退不出去。isdigit 是第 9 篇引入的字符串方法,这里是它的第二个实战场景;异常处理(如 try/except ValueError)要到第七章才正式登场,届时两种守卫方式的取舍会有完整讨论。

第 3 题(行-列建模):输出 n 行数字金字塔。 第 i 行先打印 n - i 个空格,再打印数字 i 重复 i 次(1、22、333……),完全复用第 9 篇的公式三步法。

n = 5
for i in range(1, n + 1):
    print(" " * (n - i) + str(i) * i)
    1
   22
  333
 4444
55555

边界自检:i = 1 时 4 个空格加一个 "1";i = 5 时 0 空格加五个 "5",底边打满。str(i) * i 的奥妙在于:字符串乘法要求整数乘数,这里恰好用行号当乘数,字符与数字顺手统一了。

综合编程任务:成绩统计系统

把本章所有技能装进一个程序。需求:① 反复录入成绩(0100 的整数),输入 q 结束;② 等级划分 A(90100)、B(8089)、C(7079)、D(60~69)、F(<60);③ 非数字、超范围输入要拦截并提示,不能崩溃;④ 结束后输出记录数、平均分(保留 1 位小数)、最高分、最低分与各等级人数;⑤ 一条记录都没有时给出单独提示。参考实现(Python 3.12 实测):

print("===== 成绩统计 =====")
print("逐个录入成绩(0~100),输入 q 结束")
total = 0
count = 0
highest = -1
lowest = 101
n_a = n_b = n_c = n_d = n_f = 0
while True:
    s = input("成绩:").strip()
    if s == "q":
        break
    if not s.isdigit():
        print("无效输入,请输入 0~100 的整数或 q")
        continue
    score = int(s)
    if score > 100:
        print("成绩不能超过 100")
        continue
    total += score
    count += 1
    if score > highest:
        highest = score
    if score < lowest:
        lowest = score
    if score >= 90:
        n_a += 1
    elif score >= 80:
        n_b += 1
    elif score >= 70:
        n_c += 1
    elif score >= 60:
        n_d += 1
    else:
        n_f += 1
if count:
    print(f"共 {count} 条记录,平均分 {total / count:.1f}")
    print(f"最高 {highest},最低 {lowest}")
    print(f"A(90-100):{n_a}  B(80-89):{n_b}")
    print(f"C(70-79):{n_c}  D(60-69):{n_d}  F(<60):{n_f}")
else:
    print("没有录入任何成绩")

一次完整会话(真实运行):

===== 成绩统计 =====
逐个录入成绩(0~100),输入 q 结束
成绩:85
成绩:92
成绩:57
成绩:60
成绩:101
成绩不能超过 100
成绩:abc
无效输入,请输入 0~100 的整数或 q
成绩:75
成绩:88
成绩:q
共 6 条记录,平均分 76.2
最高 92,最低 57
A(90-100):1  B(80-89):2
C(70-79):1  D(60-69):1  F(<60):1

程序把本章知识用得滴水不漏,逐段点评:录入循环是第 8/9 篇的 while True + input + break 骨架,q 的退出判断先于 isdigit(编程题 2 的坑在这里再次出现);两重守卫一前一后:isdigit 拦截非数字(配合 continue 跳过本轮),超范围判断拦截越界成绩,int(score) 永远只处理纯数字串,这正是第 9 篇输入管线(strip → isdigit → int)的展开版;等级链是测验第 1 题分支顺序的正向例子——A 到 F 的条件互斥且区间递减,任意成绩恰好落入一档;最高/最低分用两个初始哨兵值(-1 与 101)加比较更新实现,刻意不用 max/min 内置函数,为的是检验「比较 + 变量更新」的循环基本功——你把哨兵换成 None 的写法会在第四章学完比较后自然浮现;输出用 f-string 的 .1f 控制平均分精度,这是第二章的收尾技能;空数据分支 if count: 是真值测试(第二章布尔篇)在统计类程序的标配收尾。均值 76.2 可以用心算复核:六条成绩总和 457,除以 6 得 76.17 进位为 76.2。

学习自检清单

逐项自测:能不看任何参考资料完成就画勾;画不出勾的项,回读对应篇目再试,不要往下走。

  • 能默写 if/elif/else 骨架,并讲清「elif 链命中即止」与三个独立 if 的差别(第 1 篇)。
  • 能说出 match 的四个要素:case 顺序、或模式 1 | 2、守卫 if 与兜底 _,各自何时触发(第 2 篇)。
  • 能解释 while 的条件在做真值测试:空字符串、0、空容器为什么能当退出哨兵(第 3 篇 + 第二章布尔篇)。
  • 能写出 range 的三种形态,并立刻说出 range(10, 3, -2) 的结果(第 4 篇)。
  • 能现场演示 break 跳出本层、continue 跳过本轮,并说清 for-else 的 else 在什么条件下执行(第 5、6 篇)。
  • 能在白纸上先推导图案公式、再用 10 行内代码画出等腰三角形与空心矩形(第 9 篇)。
  • 能徒手写出 [x * x for x in range(5)] 与 sum(x for x in ... if ...),并说出两者对内存的不同态度(第 7 篇)。
  • 能不用复制粘贴写出「while True + input + 分发 + break」的菜单骨架,并解释为什么 break 的缩进是致命的(第 9 篇)。
  • 能处理「用户输入的不是数字」:strip → isdigit → int 的管线顺序,以及负号必须先于 isdigit 判断(第 9 篇 + 本篇编程题 2)。
  • 能完整复现本篇综合任务:不查资料、一次运行通过全部测试输入。

小结

第三章在此闭环。十篇的路线图值得最后回望一次:if/elif/else 与 match 回答「程序往哪走」,while 与 for 回答「走多少遍」,break/continue/else 回答「怎么提前收场」,嵌套与推导式回答「结构如何组合」,FizzBuzz、猜数字、图案工坊与成绩统计则证明这些结构能拼出真实可用的程序。全章没有出现函数、类与异常处理——不是它们不重要,而是流程控制必须先独立站住:第四章将把 def 扶正,让本测验里反复出现的「重复代码」第一次拥有合法的收纳容器;第七章再用 try/except 接管「用户输入不可信」这类本测验只能靠 isdigit 硬扛的场景。拿着这份自检清单回读错题,我们第四章见。