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

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

## 引言 这是第三章「流程控制」的最后一篇,收卷篇。前九篇的递进路线是:分支(第 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 篇 分支结构) ```python 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 篇 模式匹配) ```python x = 9 match x: case 1 | 2: r = "小" case _ if x > 5: r = "大" case _: r = "中" print(r) ``` 输出是?(A)`"小"` (B)`"大"` (C)`"中"` (D)报错 ### 第 3 题(出处:第 3 篇 while 循环) ```python 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) ```python print(list(range(2, 11, 3))) ``` 输出是?(A)`[2, 5, 8]` (B)`[2, 5, 8, 11]` (C)`[3, 5, 8]` (D)报错 ### 第 5 题(出处:第 5 篇 循环控制三件套) ```python n = 7 for d in range(2, n): if n % d == 0: print("合数") break else: print("质数") ``` 输出是?(A)`"合数"` (B)`"质数"` (C)什么都没有输出 (D)报错 ### 第 6 题(出处:第 6 篇 嵌套与组合) ```python 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 篇 推导式) ```python 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 篇 实战:猜数字的循环骨架) ```python 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 篇 实战:图案打印) ```python for i in range(1, 4): print("*" * i) ``` 输出是?(A)三行星号,个数依次 1、2、3 (B)三行星号,个数依次 3、2、1 (C)只有一行 3 个星号 (D)报错 ### 第 10 题(出处:综合,第 7 篇生成器表达式) ```python print(sum(x for x in range(1, 5) if x % 2 == 0)) ``` 输出是?(A)6 (B)4 (C)10 (D)报错,生成器表达式不能参与求和 ### 计分与回读表 每题 10 分。先按下表判断分数段:8 分以下,把错题对应的篇目重读一遍再返场;9~10 分,直接挑战后面的编程题。分数只说明哪些点没建起来,「回读」才是补齐动作——错题对应篇目一览: | 题号 | 考点 | 回读篇目 | |---|---|---| | 1 | `elif` 链从上到下首个命中即结束 | 第 1 篇「分支的执行流程」小节 | | 2 | `match` 的或模式、守卫(guard)与 `_` 兜底 | 第 2 篇「结构化模式」小节 | | 3 | `continue` 跳过本轮剩余语句 | 第 3 篇 + 第 5 篇「continue」小节 | | 4 | `range` 三参数:含头不含尾、步长 | 第 4 篇「range 的三种形态」 | | 5 | `for-else`:未 break 结束时执行 else | 第 5 篇「else 子句」小节 | | 6 | `break` 只终止最近一层循环 | 第 6 篇「嵌套与作用域」 | | 7 | 集合推导式与序列推导式的区别 | 第 7 篇「字典与集合推导式」 | | 8 | `while 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 是质数。 ```python for n in range(2, 101): for d in range(2, n): if n % d == 0: break else: print(n, end=" ") print() ``` ```text 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` 结束;非数字输入给出提示、不计入统计;结束后报告有效个数与总和。 ```python 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}") ``` ```text 输入整数(-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 篇的公式三步法。 ```python n = 5 for i in range(1, n + 1): print(" " * (n - i) + str(i) * i) ``` ```text 1 22 333 4444 55555 ``` 边界自检:i = 1 时 4 个空格加一个 `"1"`;i = 5 时 0 空格加五个 `"5"`,底边打满。`str(i) * i` 的奥妙在于:字符串乘法要求整数乘数,这里恰好用行号当乘数,字符与数字顺手统一了。 ## 综合编程任务:成绩统计系统 把本章所有技能装进一个程序。需求:① 反复录入成绩(0~100 的整数),输入 `q` 结束;② 等级划分 A(90~100)、B(80~89)、C(70~79)、D(60~69)、F(<60);③ 非数字、超范围输入要拦截并提示,不能崩溃;④ 结束后输出记录数、平均分(保留 1 位小数)、最高分、最低分与各等级人数;⑤ 一条记录都没有时给出单独提示。参考实现(Python 3.12 实测): ```python 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("没有录入任何成绩") ``` 一次完整会话(真实运行): ```text ===== 成绩统计 ===== 逐个录入成绩(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 硬扛的场景。拿着这份自检清单回读错题,我们第四章见。