Python 大数据分析基础 · 第 3 次课

第 3 章 程序控制结构

顺序结构 · 选择结构(if / elif / else / 嵌套)· 循环结构(while / for / range / break / continue / else / 嵌套)· 综合应用

中华女子学院6 学时(讲授 2 + 上机 4)授课人:孙琪翔2026 年秋季学期 · 第 3—4 周

本讲导览

Lesson Overview · 本章共 21 个知识点 + 9 个综合案例

前两章写的程序都是"从上到下一条道走到底",这种程序只能处理一种情况。本章给程序装上岔路口(选择)和重复器(循环)——这是"会写程序"和"只会抄程序"的分界线。

第 1 讲 · 45 分钟
顺序结构与选择结构
顺序结构 · 条件表达式 · 单分支 if · 双分支 if-else · 多分支 if-elif-else · 嵌套分支 · 三元表达式
第 2 讲 · 45 分钟
循环结构
while 三要素 · 累加器模式 · for 遍历 · range 函数 · break / continue · 循环 else · 嵌套循环
上机一 · 90 分钟
选择结构 + 基础循环
奇偶判断、成绩等级、BMI、分段函数、三角形判定、折纸珠峰、1—100 求和
上机二 · 90 分钟
综合应用
猜数字四连、简易登录系统、5 的倍数累加和、个人所得税、小明学单词
学习目标:理解顺序、选择、循环三种基本控制结构;能根据问题正确选择单分支 / 双分支 / 多分支 / 嵌套;掌握 while 三要素与 for 遍历;会用 range 控制区间与步长;理解 break、continue、循环 else 的区别;能用嵌套循环解决图形与矩阵类问题;能用本章语法解释 AI 的决策树、训练迭代与注意力计算。

本课地图

本章知识点总览

点击节点可跳转到对应讲解位置,点击三角可展开 / 收起分支。

第 3 章 程序控制结构
选择结构模块一
顺序结构:默认从上往下一行行执行
条件表达式:非零即真、非空即真
单分支 if:满足才做
双分支 if-else:二选一,else 不写条件
多分支 if-elif-else:从严到宽,命中即停
elif 顺序陷阱:85 分被判成 B
嵌套分支与连续区间陷阱
三元表达式:一句话的二选一(选讲)
循环结构模块二
循环 = 循环体 + 终止条件
while:初值 / 条件 / 更新,三要素缺一不可
累加器与计数器:清零在外,累加在内
for:把序列里的元素一个个取出来
range():含头不含尾,步长可为负
break / continue:结束整个循环 vs 跳过本轮
循环 else:有 break 就没 else
嵌套循环:总次数是 n × m
综合应用模块三
三角形判定(while 重复 5 次)
折纸珠峰(指数增长)
猜数字四连:if / while / for / while True + break
简易登录系统(for-else)
个人所得税(简化三档)
AI 渗透(A 类)模块四
if / elif / else → 决策树
while → 训练迭代(本章高潮)
for → 遍历数据集与准确率
range 步长 → 分批 batch
break → 早停 Early Stopping
continue → 跳过脏数据
循环 else → 这一轮完整跑完了吗
嵌套循环 → 注意力矩阵的平方代价
说明:本章依据课程课件《3-程序控制结构》与《Python 编程与数据分析基础》(陈洁、刘姝,清华大学出版社)第 3 章整理;页面中每段代码均为真实运行后回填的结果。

进度表点名的 7 项 → 本节落点

#进度表点名内容本页位置
1双分支选择结构模块一 §1.4
2多分支结构选择结构模块一 §1.5 / §1.6
3While 循环结构模块二 §2.2 / §2.3
4for 循环结构模块二 §2.4 / §2.5
5break、continue、else模块二 §2.6 / §2.7
6嵌套的循环结构模块二 §2.8
7程序控制结构综合应用模块三
本节还保留了教材里有、但进度表未点名的 顺序结构、单分支 if、range()、嵌套分支——按"原有内容不能少"的原则全部讲,但顺序结构与单分支会讲得快一些。

顺序结构与选择结构

第 1 讲 · 45 分钟 · 顺序结构 · 条件表达式 · 单分支 · 双分支 · 多分支 · 嵌套 · 三元表达式

1.1 顺序结构:程序的默认形态

程序从上往下、一行一行、不跳过、不回头地执行——这就是顺序结构,它是所有程序的默认形态。分支和循环都是在这个"默认流水"上打岔:先认清默认,才知道岔在哪儿。

例 2-8(复习)计算圆的面积
# 顺序结构:三条语句依次执行
r = 5
pi = 3.14159
area = pi * r * r
print("圆的面积:", area)
运行结果(实测)
圆的面积: 78.53975
本节小结:顺序结构 = 从上到下一行行执行。它是"默认流水",分支和循环都是在这条流水上"打岔",所以判断一段代码有没有写歪,先看它是不是默认从上到下。
配套视频 会考 Python 系列 · 顺序结构及应用 B站 · 12:22 · 用流程图讲清"一条道走到底"

1.2 条件表达式:什么东西能当条件

教材原话:"所有的 Python 合法表达式(算术表达式、关系表达式、逻辑表达式等,包括单个常量、变量或函数)都可以作为条件表达式。"也就是说,if 后面不一定要写 > < ==,任何值都能当条件,Python 会把它转成 True / False

当条件时说明
00.0False数字"非零即真"
非零数字Trueif x % 2: 等价于 if x % 2 != 0:
""(空串)False字符串"非空即真"
非空字符串True常用作"用户到底输入了没有"
NoneFalse空值
空列表 []、空字典 {}False第 4 章还会遇到
用 bool() 验证"什么算真、什么算假"
print(bool(0), bool(2), bool(-1))
print(bool(""), bool("hi"))
print(bool(None), bool([]), bool([0]))
运行结果(实测)
False True True
False True
False False True
本节小结:条件的判定结果只有 True / False 两种;数字非零即真,字符串非空即真None 与空容器为假。这一条到第 8 章 Pandas 的 dropna() 还在用。

1.3 单分支选择结构:满足才做

语法:if 条件表达式: 换行缩进写语句块。三条铁律(教材原文):① Python 靠缩进表达代码层次,不是靠大括号;② 行尾冒号表示缩进开始,缩进结束表示代码块结束;③ 同一级别的语句块,缩进量必须相同

单分支:输入 8(偶数时会多打印一行)
x = int(input("输入一个数字:"))
if x % 2 == 0:
    print("偶数")
print("判断结束")        # 这一行已经退出 if 了,无论如何都执行
运行结果(实测 · 输入 8)
输入一个数字:8
偶数
判断结束
同一段代码:输入 7(奇数时不打印"偶数")
x = int(input("输入一个数字:"))
if x % 2 == 0:
    print("偶数")
print("判断结束")
运行结果(实测 · 输入 7)
输入一个数字:7
判断结束
缩进写错,两种下场:一种是直接报 IndentationError(好办,报错就改);另一种是不报错、但逻辑悄悄错了(难查)。把 print("判断结束") 缩进到 if 里面,它就变成"只有偶数才打印"——程序照跑,答案错了。
本节小结冒号起缩进,去缩进结束代码块,同级必须对齐。 单分支只处理"满足时做什么",不满足就什么都不做、直接往下走。

1.4 双分支选择结构 进度表点名

两个分支互斥且穷尽:条件成立走语句块 1,否则走语句块 2,二选一,没有第三种可能。

例 3-1:判断奇偶数(输入 7)
x = int(input())
if x % 2 == 0:
    print("偶数")
else:
    print("奇数")
运行结果(实测 · 输入 7)
7
奇数
最常见的错法:给 else 加条件。 else x % 2 == 1: 会直接 SyntaxError。记住:else 的含义是"以上都不成立",所以它后面永远不写条件
本节小结:二选一用 if-else;else 后面不写条件,写了就是语法错误。两个分支互斥且穷尽,程序一定会走其中一条。
配套视频 黑马程序员 · 第 32 讲 if-else 组合判断语句 B站 · 另有第 31、33 讲配套案例(成年人判断、我要买票吗)

1.5 多分支选择结构 进度表点名

语法为 if / elif / elif / … / else,其中 else 部分可选。教材原话:"如果所有表达式都为 False,且存在 else 部分,则执行语句块 n。"

例 3-2:成绩等级判断(输入 85)
score = int(input("请输入成绩:"))
if score > 100 or score < 0:
    result = "输入错误,成绩必须在 0 到 100 之间"
elif 80 <= score <= 100:
    result = "A"
elif 60 <= score < 80:
    result = "B"
else:
    result = "C"
print("成绩等级:", result)
运行结果(实测 · 输入 85)
请输入成绩:85
成绩等级: A
边界值全测:把 8 个关键值一次性验完
# 合法性检查必须放在第一个:否则 101 会被后面的分支"接走"
for score in [101, -1, 100, 80, 79, 60, 59, 0]:
    if score > 100 or score < 0:
        result = "输入错误"
    elif 80 <= score <= 100:
        result = "A"
    elif 60 <= score < 80:
        result = "B"
    else:
        result = "C"
    print("输入", score, "→", result)
运行结果(实测)
输入 101 → 输入错误
输入 -1 → 输入错误
输入 100 → A
输入 80 → A
输入 79 → B
输入 60 → B
输入 59 → C
输入 0 → C
本节小结:多分支是"从上往下、命中即停",所以条件必须从最严格排到最宽泛合法性检查(>100 或 <0)永远放第一个。省略 else 时,可能出现"一个分支都不执行"的情况。
配套视频 黑马程序员 · 第 34 讲 if-elif-else 组合使用的语法 B站 · 案例见第 35 讲"猜猜心里数字"

1.6 必做演示:elif 顺序陷阱 学生 100% 会踩

把宽条件写在前面,后面的严格条件就永远没有机会判断。下面把错版和正版并排跑一遍。

错版 vs 正版:同样 6 个分数,两套结果
for score in [95, 85, 80, 75, 60, 45]:
    # ❌ 错版:宽条件在前,85 分被"及格"先截走
    if score >= 60:
        wrong = "B"
    elif score >= 80:
        wrong = "A"        # 永远到不了这里
    else:
        wrong = "C"

    # ✅ 正版:严格条件在前
    if score >= 80:
        right = "A"
    elif score >= 60:
        right = "B"
    else:
        right = "C"

    print(score, "分 → 错版:", wrong, " 正版:", right)
运行结果(实测)
95 分 → 错版: B  正版: A
85 分 → 错版: B  正版: A
80 分 → 错版: B  正版: A
75 分 → 错版: B  正版: B
60 分 → 错版: B  正版: B
45 分 → 错版: C  正版: C
本节小结elif 从上往下互斥匹配,命中就停,所以顺序错了答案就错,而且程序不会报错——这类错误只能靠"边界值测试"抓出来。记住口诀:严格在前,宽泛在后

1.7 嵌套的分支结构 教材有 · 进度表未点名

多分支能做的事,嵌套分支也能做,但可读性差得多。判断标准很简单:条件是"平级互斥"(A / B / C 选一个)就用 elif;条件有"层次"(先分类、类内再细分)才用嵌套

嵌套分支:地铁票价(先判断是否学生,再按里程计价)
is_student = input("是否学生(y/n):") == "y"
distance = float(input("请输入里程(km):"))

if is_student:
    # 外层 if 的语句块里,又套了一整套 if-elif-else
    if distance <= 6:
        price = 2.0
    elif distance <= 12:
        price = 3.0
    else:
        price = 4.0
    price = price * 0.5          # 学生半价
    note = "学生票五折"
else:
    if distance <= 6:
        price = 2.0
    elif distance <= 12:
        price = 3.0
    else:
        price = 4.0
    note = "全价"

print("票价:%.1f 元(%s)" % (price, note))
运行结果(实测 · 学生 y,里程 10 km)
是否学生(y/n):y
请输入里程(km):10
票价:1.5 元(学生票五折)

例:BMI 判断(连续区间用"单边比较链")

BMI 判断(体重 70 kg,身高 1.70 m)
weight = float(input("请输入体重(kg):"))
height = float(input("请输入身高(m):"))
bmi = weight / (height ** 2)
print("BMI = %.2f" % bmi)

# 连续区间:只写下界,上界由上一行"继承"
if bmi < 18.5:
    print("过轻")
elif bmi < 24:
    print("正常")
elif bmi < 28:
    print("过重")
else:
    print("肥胖")
运行结果(实测)
请输入体重(kg):70
请输入身高(m):1.70
BMI = 24.22
过重
关键对比:BMI = 23.95 时,两种写法结果不一样
bmi = 23.95          # 明明属于"正常"范围

# PPT 风格:显式写双端区间 —— 会漏掉 23.91~23.99 这一小段
if bmi < 18.5:
    r1 = "过轻"
elif 18.5 <= bmi <= 23.9:
    r1 = "正常"
elif 24 <= bmi < 28:
    r1 = "过重"
else:
    r1 = "肥胖"      # ← 掉进这里了

# 推荐写法:单边比较 + elif 链,区间无缝衔接
if bmi < 18.5:
    r2 = "过轻"
elif bmi < 24:
    r2 = "正常"
elif bmi < 28:
    r2 = "过重"
else:
    r2 = "肥胖"

print("BMI =", bmi)
print("双端区间写法 →", r1)
print("单边比较写法 →", r2)
运行结果(实测)
BMI = 23.95
双端区间写法 → 肥胖
单边比较写法 → 正常
本节小结:嵌套能实现多分支,但层次一多就没人看得懂。写连续区间时用"单边比较 + elif 链"(bmi < 18.5 / < 24 / < 28)比写双端区间安全——双端区间会在两个端点之间留下缝,数据掉进缝里就被 else 兜走了。
配套视频 黑马程序员 · 第 36 讲 判断语句的嵌套 B站 · 综合案例见第 37 讲

1.8 三元表达式:一句话的二选一 拓展 · 教材未收录

写法:结果 = 值1 if 条件 else 值2。等价于 4 行 if-else,但只适合一行能装下的简单二选一;嵌套三元表达式是著名的"可读性杀手",不提倡。

三元表达式(输入 8)
x = int(input("请输入一个整数:"))
result = "偶数" if x % 2 == 0 else "奇数"
print(result)
运行结果(实测)
请输入一个整数:8
偶数
本节小结:三元表达式是 if-else 的"一句话写法",只在简单二选一赋值时使用;超过一层嵌套就老老实实写 if-else。考试如果没要求,用它不加分,写错要扣分。
配套视频 黑马程序员 · 第 34 讲 if-elif-else 组合使用的语法 B站 · 先看 if-else,再理解"三元表达式就是把 if-else 写成一行"

循环结构

第 2 讲 · 45 分钟 · while · 累加器 · for · range · break / continue · else · 嵌套循环

2.1 什么是循环

教材原话:"循环结构是指在一定条件下重复执行某段代码的流程结构。其中,被重复执行的代码块称为循环体,判断是否继续执行的条件称为终止条件。" Python 只有两种循环语句:whilefor

本节小结循环 = 循环体 + 终止条件。缺了终止条件就是死循环——这是初学者把电脑跑飞的唯一原因。
配套视频 黑马程序员 · 第 38 讲 while 循环的基础应用 B站 · 先建立"重复做"的直觉

2.2 while 语句 进度表点名

while 三要素(写在黑板上的那种):① 设置循环变量并赋初值 → ② 根据循环变量判断条件 → ③ 在循环体中更新循环变量。教材特别强调:循环体中必须有改变循环条件的语句,否则会进入死循环

用 while 打印 5 次 Hello
i = 1                # ① 初值
while i <= 5:        # ② 条件
    print("Hello!", i)
    i = i + 1        # ③ 更新(少了这一句就是死循环)
运行结果(实测)
Hello! 1
Hello! 2
Hello! 3
Hello! 4
Hello! 5
循环次数i条件 i <= 5是否进入循环执行更新后 i
1True进入print2
2True进入print3
3True进入print4
4True进入print5
5True进入print6
退出6False不进入

把上面这张表带学生走一遍,比讲十遍语法管用。下面这段是故意写错的死循环反例(别运行,或在 IDLE 里用 Ctrl + C 停):

❌ 死循环反例:忘了更新循环变量不要运行
i = 1
while i <= 5:
    print("Hello!", i)
    # 忘了写 i = i + 1  →  条件永远成立
本节小结while 三要素:初值、条件、更新。少第三个要素就是死循环;少第一个要素直接 NameError。判断该不该用 while 只问一句:"我一开始就知道要跑几遍吗?"
配套视频 黑马程序员 · 第 38 讲 while 循环的基础应用 B站 · 第 41 讲为 while 嵌套

2.3 累加器与计数器模式 本章最核心的套路

"求和、求平均、统计个数"这三件事占课后作业的一半,写法都是同一个套路:计数器 i 负责数到几,累加器 total 负责把结果攒起来

例 3-7:1 + 2 + … + 100(while 版 + for 版)
# while 版
total = 0                 # 累加器:清零必须放在循环外面
i = 1
while i <= 100:
    total += i
    i += 1
print("while 版:", total)

# for 版:同样的事,计数交给 Python 管
total = 0
for i in range(1, 101):
    total += i
print("for 版:", total)
运行结果(实测)
while 版: 5050
for 版: 5050
经典错误:total = 0 写进了循环体
# ✅ 正确:清零在循环外
total = 0
i = 1
while i <= 10:
    total += i
    i += 1
print("正确写法 1~10 累加:", total)

# ❌ 错误:清零写进了循环体,每轮都从头开始
i = 1
while i <= 10:
    total = 0            # ← 位置错了
    total += i
    i += 1
print("错误写法 1~10 累加:", total)
运行结果(实测)
正确写法 1~10 累加: 55
错误写法 1~10 累加: 10
本节小结清零在外,累加在内。累加器 total = 0 必须在循环之前,计数器更新 i = i + 1 必须在循环体里面。错误写法不报错、只是答案不对——所以要养成"用已知答案的小数据自测"的习惯。

2.4 for 语句 进度表点名

语法:for 变量 in 序列或迭代对象: 循环体。变量从序列中依次取值,取完就结束——所以 for 循环永远不会死循环

for 遍历一个列表
for name in ['zhang', 'li', 'wang', 'zhao']:
    print(name)
运行结果(实测)
zhang
li
wang
zhao
场景用哪个理由
次数已知(跑 5 次、遍历列表)for计数交给 Python,不会写错、不会死循环
次数未知(猜到才停、输入 q 才停)while条件要运行时才知道
退出条件在循环体中间while True + breakfor 做不到"中途退出"的通用写法
本节小结for 是"把序列里的东西一个个取出来",计数由 Python 管,所以不会死循环。已知次数用 for,未知次数用 while——这句话是本章选择题的常客。
配套视频 黑马程序员 · 第 43 讲 for 循环的基础语法 B站 · 案例见第 44 讲;变量作用域见第 46 讲

2.5 range() 函数 教材专页

写法 range(start, stop, step):start 起始值(默认 0)、stop 终止值(取不到)、step 步长(默认 1,可为负)。

range 六种写法实测(用 list() 才能看见内容)
print("range(5)          →", list(range(5)))
print("range(0, 5)       →", list(range(0, 5)))
print("range(0, 5, 2)    →", list(range(0, 5, 2)))
print("range(10, 0, -2)  →", list(range(10, 0, -2)))
print("range(0, -10, -1) →", list(range(0, -10, -1)))
print("range(1, 10) 一共几个数?→", len(range(1, 10)))
运行结果(实测)
range(5)          → [0, 1, 2, 3, 4]
range(0, 5)       → [0, 1, 2, 3, 4]
range(0, 5, 2)    → [0, 2, 4]
range(10, 0, -2)  → [10, 8, 6, 4, 2]
range(0, -10, -1) → [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
range(1, 10) 一共几个数?→ 9
四个必讲的坑:① stop 取不到——range(1, 11) 才是 1 到 10;② range() 返回的是 range 对象,要看得见就套 list();③ 步长不能为 0,否则 ValueError: range() arg 3 must not be zero;④ 递减时 start 必须大于 stop,range(0, 10, -1) 一个数都不产生。
本节小结含头不含尾,步长默认 1,可以为负。想数到几,stop 就写"几 + 1";想只要偶数,用 range(0, n, 2),比在循环里 if 筛一遍更省事。

2.6 break 与 continue 进度表点名

教材原文:continue 跳过本次循环剩余语句、进入下一轮循环;break 跳出循环、进入循环结构后面的语句。多重循环中 break 只能跳出自己所属的那一层;两者通常和 if 配合使用。

break:遇到 5 就停 | continue:跳过奇数
# break:i 到 5 就整个结束,后面的 5~9 都不打印
for i in range(10):
    if i == 5:
        break
    print(i, end=",")
print()

# continue:奇数跳过这一轮,偶数照常打印
for i in range(1, 11):
    if i % 2 == 1:
        continue
    print(i, end=",")
print()
运行结果(实测)
0,1,2,3,4,
2,4,6,8,10,
多重循环里,break 只跳出一层
for i in range(5):
    for j in range(3):
        if j == 2:
            break            # 只跳出内层 j 循环,外层 i 继续
        print(i, j)
运行结果(实测)
0 0
0 1
1 0
1 1
2 0
2 1
3 0
3 1
4 0
4 1
while + continue 的坑:如果 i = i + 1 写在 continue 后面,那一句永远轮不到执行 → 死循环。在 while 里用 continue 之前,先确认递增语句不会被跳过。
本节小结break = 整个撂挑子不干了;continue = 这一次跳过、下一轮照常。 多重循环里 break 只跳一层,要跳多层得用标志变量;while 里用 continue 要格外小心递增语句。

2.7 循环的 else 子句 进度表点名

教材三条说明:① else 子句是可选的;② 遍历完毕自然结束的会执行 else;③ 执行了 break 提前结束的不会执行 else。

同样两个循环,一个有 break、一个只有 continue
print("—— 有 break:else 不执行 ——")
for i in range(10):
    if i == 5:
        break
    print(i, end=",")
else:
    print("Finish!")
print()

print("—— 没有 break:else 执行(continue 不影响 else)——")
for i in range(10):
    if i == 5:
        continue
    print(i, end=",")
else:
    print("Finish!")
print()
运行结果(实测)
—— 有 break:else 不执行 ——
0,1,2,3,4,
—— 没有 break:else 执行(continue 不影响 else)——
0,1,2,3,4,6,7,8,9,Finish!
经典应用:素数判断(输入 17)
n = int(input("请输入一个大于 1 的整数:"))
for i in range(2, int(n ** 0.5) + 1):
    if n % i == 0:
        print(n, "不是素数")
        break
else:
    print(n, "是素数")        # 找了一圈都没找到因子 → 是素数
运行结果(实测 · 输入 17;输入 91 会输出"91 不是素数")
请输入一个大于 1 的整数:17
17 是素数
本节小结有 break 就没 else,没 break 才有 else。else 挂在循环后面而不是 if 后面,含义是"完整跑完了 / 找了一圈没找到"——这是 Python 独有、也最容易考倒人的写法。
配套视频 python 中 for 循环和 else 的搭配使用 B站 · 7:48 · 专门讲循环 else 的少见短片

2.8 嵌套的循环结构 进度表点名

教材原话:"如果外层循环执行 n 次,内层循环执行 m 次,则整个循环要执行 n × m 次。"图形题(三角、矩形、菱形)全靠嵌套循环。

例 3-10:九九乘法口诀表
for i in range(1, 10):
    for j in range(1, i + 1):       # ← 内层次数随外层变化,这是"三角形"
        print(j, "×", i, "=", i * j, end="\t")
    print()                          # 外层每走一步就换行
运行结果(实测,9 行)
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) 依赖外层变量,就能打出三角形;两层都写固定范围就是矩形。
配套视频 黑马程序员 · 第 47 讲 for 循环的嵌套使用 B站 · 打印九九乘法表见第 48 讲;while 嵌套见第 41、42 讲

综合应用(上机 4 学时)

对应进度表第 7 项:程序控制结构综合应用 · 9 个案例 · 2 次上机

编号任务对应知识点难度要求
U1-01判断奇偶数(例 3-1 复现)双分支必做
U1-02成绩等级(例 3-2),测 8 个边界值多分支★★必做
U1-03BMI 判断,用单边比较写法多分支★★必做
U1-04分段函数(习题 1)多分支★★必做
U1-05三角形判定,重复执行 5 次分支 + while★★★必做
U1-06打印 5 次 Hello + 手填执行过程表while 三要素必做
U1-07折纸珠峰(例 3-6)while★★必做
U1-081+2+…+100(while 版 + for 版)累加器★★必做
U1-09故意写一个死循环,用 Ctrl+C 停死循环识别必做
U2-01~04猜数字四连(if / while / for / while True + break)综合★★~★★★必做
U2-05简易登录系统(最多 3 次)for-else★★★必做
U2-06作业 1:1—100 中 5 的倍数累加和for + range 步长★★必做
U2-07作业 2:个人所得税(简化三档)多分支★★★必做
U2-08/09小明学单词(while 版 / for 版)while vs for★★★选做

3.1 三角形判定(while 重复执行 5 次)

根据三边判断能否构成三角形:可以则用海伦公式算面积(保留 2 位小数),否则提示"无法构成三角形",并要求重复执行 5 次——这里正好把"分支 + 循环"合起来用。

例 3-3(PPT S10 原题):循环 5 次版(依次输入 3 4 5|1 2 3|6 8 10|2 2 5|5 5 5)
import math

i = 1
while i <= 5:
    a = float(input("请输入第 1 条边:"))
    b = float(input("请输入第 2 条边:"))
    c = float(input("请输入第 3 条边:"))

    if a + b > c and a + c > b and b + c > a:
        p = (a + b + c) / 2
        area = math.sqrt(p * (p - a) * (p - b) * (p - c))
        print("可以构成三角形,面积为:%.2f" % area)
    else:
        print("无法构成三角形")

    i = i + 1
运行结果(实测 · 5 组输入)
请输入第 1 条边:3
请输入第 2 条边:4
请输入第 3 条边:5
可以构成三角形,面积为:6.00
请输入第 1 条边:1
请输入第 2 条边:2
请输入第 3 条边:3
无法构成三角形
请输入第 1 条边:6
请输入第 2 条边:8
请输入第 3 条边:10
可以构成三角形,面积为:24.00
请输入第 1 条边:2
请输入第 2 条边:2
请输入第 3 条边:5
无法构成三角形
请输入第 1 条边:5
请输入第 2 条边:5
请输入第 3 条边:5
可以构成三角形,面积为:10.83
本节小结判三角形看"任意两边之和大于第三边",写成 a+b>c and a+c>b and b+c>a 最稳;循环负责"做几遍",分支负责"这一遍怎么办"——这是综合题的标准骨架。
配套视频 黑马程序员 · 第 37 讲 判断语句综合案例 B站 · 先看判断逻辑,再套 while 循环

3.2 折纸珠峰:指数增长的威力

一张 0.1 毫米厚的纸对折多少次能超过珠峰(8848.86 米)?答案会让学生吃惊。

例 3-6:折纸珠峰
h = 0.0001            # 0.1 毫米换算成米
count = 0
while h < 8848.86:    # 次数未知 → 用 while
    h = h * 2
    count = count + 1
print("需要对折", count, "次")
print("此时厚度为", round(h, 2), "米")
运行结果(实测)
需要对折 27 次
此时厚度为 13421.77 米
本节小结次数未知的"翻倍/减半"问题一律用 while;因为每次翻倍是指数增长(2²⁷ ≈ 1.34 亿),所以只需要 27 次。同一个数学模型还出现在 AI 算力增长、病毒传播里。
配套视频 黑马程序员 · 第 38 讲 while 循环的基础应用 B站 · 与本节"次数未知用 while"的判断标准一致

3.3 猜数字四连:同一个需求,四种写法

把四份代码并排看,就能体会到"什么时候该用哪个结构"。为了课堂可复现,下面用 seed(1) 固定随机种子(随机数 x = 17);你自己的程序不要写 seed,否则每次答案都一样。

① 例 3-3:只猜一次(if)
from random import randint, seed
seed(1)                       # 课堂演示用;自己写程序时删掉这一行
x = randint(0, 100)

guess_number = int(input("输入一个 0~100 之间的整数:"))
if guess_number == x:
    print("Bingo!")
elif guess_number > x:
    print("Too large!")
else:
    print("Too small!")
print("(演示提示:随机数 x =", x, ")")
运行结果(实测 · 猜 50)
输入一个 0~100 之间的整数:50
Too large!
(演示提示:随机数 x = 17 )
② 例 3-5:while 版,可以猜 5 次
from random import randint, seed
seed(1)
x = randint(0, 100)

i = 0
while i < 5:
    guess = int(input("请输入一个 0~100 的整数:"))
    if guess == x:
        print("Bingo!")
    elif guess > x:
        print("Too large!")
    else:
        print("Too small!")
    i = i + 1
print("(演示提示:随机数 x =", x, ")")
运行结果(实测 · 依次猜 50 25 12 17 1)
请输入一个 0~100 的整数:50
Too large!
请输入一个 0~100 的整数:25
Too large!
请输入一个 0~100 的整数:12
Too small!
请输入一个 0~100 的整数:17
Bingo!
请输入一个 0~100 的整数:1
Too small!
(演示提示:随机数 x = 17 )
③ 例 3-8:for 版,同样猜 5 次(计数交给 Python)
from random import randint, seed
seed(1)
x = randint(0, 100)

for i in range(5):            # 与上面的 while 版完全等价
    guess = int(input("请输入一个 0~100 的整数:"))
    if guess == x:
        print("Bingo!")
    elif guess > x:
        print("Too large!")
    else:
        print("Too small!")
print("(演示提示:随机数 x =", x, ")")
运行结果(实测)
请输入一个 0~100 的整数:50
Too large!
请输入一个 0~100 的整数:25
Too large!
请输入一个 0~100 的整数:12
Too small!
请输入一个 0~100 的整数:17
Bingo!
请输入一个 0~100 的整数:1
Too small!
(演示提示:随机数 x = 17 )
④ 例 3-9:while True + break,猜中或输入 q 才结束
from random import randint, seed
seed(1)
x = randint(0, 100)

while True:                                   # 次数完全未知
    s = input("请输入一个 0~100 的整数(输入 q 退出):")
    if s == "q":
        print("游戏结束,答案是", x)
        break
    guess = int(s)
    if guess == x:
        print("Bingo!")
        break                                 # 猜中就停
    elif guess > x:
        print("Too large!")
    else:
        print("Too small!")
运行结果(实测 · 先猜 50,再猜 17)
请输入一个 0~100 的整数(输入 q 退出):50
Too large!
请输入一个 0~100 的整数(输入 q 退出):17
Bingo!
本节小结:同一个需求四种写法——只猜一次用 if;次数已知用 for(或 while 计数);次数未知用 while True + break。区别只在"什么时候停",判断逻辑完全一样。
配套视频 黑马程序员 · 第 40 讲 while 循环猜数字案例 B站 · 第 35 讲为 if 版猜数字

3.4 简易登录系统(for-else 的漂亮用法)

要求:用户名密码正确提示"登录成功";最多输入三次;三次都错提示"失败超过三次,请稍后再试!"——"三次都没 break"这件事,正好用 for 的 else 表达

前两次输错、第三次输对
USERNAME = "admin"
PASSWORD = "123456"

for i in range(3):
    u = input("用户名:")
    p = input("密码:")
    if u == USERNAME and p == PASSWORD:
        print("登录成功")
        break
    else:
        left = 2 - i
        if left > 0:
            print("用户名或密码错误,还剩", left, "次机会")
else:
    print("失败超过三次,请稍后再试!")
运行结果(实测)
用户名:admin
密码:wrong
用户名或密码错误,还剩 2 次机会
用户名:admin
密码:123456wrong
用户名或密码错误,还剩 1 次机会
用户名:admin
密码:123456
登录成功
三次全错:for-else 生效
USERNAME = "admin"
PASSWORD = "123456"

for i in range(3):
    u = input("用户名:")
    p = input("密码:")
    if u == USERNAME and p == PASSWORD:
        print("登录成功")
        break
    else:
        left = 2 - i
        if left > 0:
            print("用户名或密码错误,还剩", left, "次机会")
else:
    print("失败超过三次,请稍后再试!")
运行结果(实测 · 三次全错)
用户名:admin
密码:x
用户名或密码错误,还剩 2 次机会
用户名:admin
密码:y
用户名或密码错误,还剩 1 次机会
用户名:admin
密码:z
失败超过三次,请稍后再试!
本节小结"三次都没成功"用 for-else 表达最优雅——break 表示成功提前退出,else 表示全错。这也是循环 else 在真实代码里最常见的用途。
配套视频 黑马程序员 · 第 50 讲 循环综合案例 B站 · 循环 + 分支的综合运用

3.5 作业 1:1—100 中 5 的倍数的累加和

同一道题三种写法,正好对比"用 if 筛"和"用步长直接生成"的差别。

三种写法对比(正确答案 1050)
total = 0
for i in range(1, 101):       # 写法 1:全部遍历 + if 筛选
    if i % 5 == 0:
        total += i
print("写法 1(if 筛选)    :", total)

total = 0
for i in range(5, 101, 5):    # 写法 2:直接用步长(最优)
    total += i
print("写法 2(range 步长) :", total)

print("写法 3(一行搞定)   :", sum(range(5, 101, 5)))
运行结果(实测)
写法 1(if 筛选)    : 1050
写法 2(range 步长) : 1050
写法 3(一行搞定)   : 1050
本节小结能控制步长的场景就不要用 if 再筛一遍——range(5, 101, 5) 直接生成 5, 10, …, 100,代码更短、跑得更快。三种写法答案必须一致(1050),不一致就说明写错了。
配套视频 黑马程序员 · 第 45 讲 range 语句 B站 · 重点看步长参数

3.6 作业 2:个人所得税(简化三档)

税率分三档(单位:万元):x ≤ 3.6 时 x × 3%;3.6 < x ≤ 14.4 时 3.6 × 3% + (x − 3.6) × 10%;x > 14.4 时再加 (x − 14.4) × 20%。输入为负数时提示"输入值错误"。

三点必须说清:① 单位是万元,按元算必错;② 这是简化的三档模型,真实个税是七级超额累进、还有起征点和专项扣除,不要拿去真算工资;③ 题目要求"定义函数",而函数要到第 5 章才讲——这里只要求会调用,看懂结构即可。
输入应纳税所得额 20(万元)
def tax(x):                  # 函数第 5 章才正式讲,这里先"会用"
    if x < 0:
        return "输入值错误"
    if x <= 3.6:
        y = x * 0.03
    elif x <= 14.4:
        y = 3.6 * 0.03 + (x - 3.6) * 0.10
    else:
        y = 3.6 * 0.03 + (14.4 - 3.6) * 0.10 + (x - 14.4) * 0.20
    return round(y, 4)

x = float(input("请输入应纳税所得额(万元):"))
print("应缴个税:", tax(x))
运行结果(实测 · 输入 20 → 2.308 万元)
请输入应纳税所得额(万元):20
应缴个税: 2.308
本节小结超额累进 = 分段计算再相加,用 if-elif-else 从"最低档"往上写;边界值(3.6、14.4)必须单独测。单位、模型适用范围要在题目里写清楚——这题不改单位就会被学生当成真实税单。
配套视频 黑马程序员 · 第 34 讲 if-elif-else 组合使用的语法 B站 · 多分支写法直接复用

3.7 选做题:小明学单词(while 版 vs for 版)

学习 1000 个单词,每天随机学 0—50 个:while 版是"学到 1000 个或满 30 天为止",for 版是"固定 30 天"。这两题是while(条件未知)vs for(次数已知)最好的对照。

while 版:累计 ≥ 1000 或满 30 天就停(固定随机种子 7)
from random import randint, seed
seed(7)                       # 固定种子,方便课堂复现;自己写时删掉

total = 0
day = 0
while total < 1000 and day < 30:
    day += 1
    n = randint(0, 50)
    total += n
print("共用", day, "天,累计学习", total, "个单词")
运行结果(实测)
共用 30 天,累计学习 576 个单词
for 版:固定 30 天
from random import randint, seed
seed(7)

total = 0
for day in range(1, 31):
    n = randint(0, 50)
    total += n
print("30 天共学习", total, "个单词")
运行结果(实测)
30 天共学习 576 个单词
本节小结同一个问题,"什么时候停"决定了用哪个循环:条件是"累计够 1000 就停"→ 用 while;"固定跑 30 天"→ 用 for。随机函数每次结果不同是正常的,加了 seed() 才能复现。
配套视频 黑马程序员 · 第 43 讲 for 循环的基础语法 B站 · 与 while 版第 38 讲对照看

AI 渗透(A 类 · 手写 AI 原理)

本章属于基础阶段:学生不使用 AI 工具,只用手写的循环与分支,把 AI 的内部机制跑出来看

这 8 个演示不需要任何 AI 库、不联网、不调用任何模型——全部用本章的 if 和循环手写。目的是让学生明白:AI 内部最核心的机制,其实是你这两节课学的语法。

AI-1 决策树 = 一串 if / elif / else

决策树的每一个节点就是一次 if 判断,每一条从根到叶的路径就是一串 elif。机器学习里最"可解释"的模型,本质就是嵌套分支。

用"是否录取"举例(输入 GPA 4.6、论文 2 篇)
gpa = float(input("请输入 GPA(0-5):"))
paper = int(input("请输入论文数量:"))

if gpa >= 4.5:
    if paper >= 2:
        print("决策:直接录取")
    else:
        print("决策:待定,需面试")
elif gpa >= 3.5:
    if paper >= 3:
        print("决策:待定,需面试")
    else:
        print("决策:不录取")
else:
    print("决策:不录取")
运行结果(实测)
请输入 GPA(0-5):4.6
请输入论文数量:2
决策:直接录取
本节小结决策树 = 嵌套 if-else 树,节点判断特征、叶子给出结论;随机森林 = 很多棵这样的树投票。你今天写的嵌套 if,就是机器学习里最可解释的模型。
配套视频 黑马程序员 · 第 36 讲 判断语句的嵌套 B站 · 决策树的外形就是嵌套判断

AI-2 while = 训练迭代 本章 AI 高潮

AI"训练"的本质:重复同一件事,每次改一点点,直到误差够小。已知 y = 5x,让程序从 w = 1.0 开始自己把 w 学到 5.0。

三要素对上训练:初值 = 参数初始化,条件 = 收敛判据,更新 = 梯度下降
x = 2.0
y_true = 10.0
w = 1.0            # 循环变量:权重初值(参数初始化)
lr = 0.1           # 学习率
epoch = 0          # 训练轮数计数器

while abs(w * x - y_true) > 0.01:        # 终止条件:误差足够小
    pred = w * x
    w = w - lr * (pred - y_true) * x     # 更新:梯度下降
    epoch += 1
    print("第 %2d 轮:w = %.4f,预测值 = %.4f,误差 = %.4f"
          % (epoch, w, w * x, abs(w * x - y_true)))

print("收敛!共训练 %d 轮,最终 w = %.4f(理论答案 5.0)" % (epoch, w))
运行结果(实测)
第  1 轮:w = 2.6000,预测值 = 5.2000,误差 = 4.8000
第  2 轮:w = 3.5600,预测值 = 7.1200,误差 = 2.8800
第  3 轮:w = 4.1360,预测值 = 8.2720,误差 = 1.7280
第  4 轮:w = 4.4816,预测值 = 8.9632,误差 = 1.0368
第  5 轮:w = 4.6890,预测值 = 9.3779,误差 = 0.6221
第  6 轮:w = 4.8134,预测值 = 9.6268,误差 = 0.3732
第  7 轮:w = 4.8880,预测值 = 9.7761,误差 = 0.2239
第  8 轮:w = 4.9328,预测值 = 9.8656,误差 = 0.1344
第  9 轮:w = 4.9597,预测值 = 9.9194,误差 = 0.0806
第 10 轮:w = 4.9758,预测值 = 9.9516,误差 = 0.0484
第 11 轮:w = 4.9855,预测值 = 9.9710,误差 = 0.0290
第 12 轮:w = 4.9913,预测值 = 9.9826,误差 = 0.0174
第 13 轮:w = 4.9948,预测值 = 9.9896,误差 = 0.0104
第 14 轮:w = 4.9969,预测值 = 9.9937,误差 = 0.0063
收敛!共训练 14 轮,最终 w = 4.9969(理论答案 5.0)
lr 从 0.1 改成 0.05,收敛轮数立刻变多;改成 1.5 甚至可能"飞出去"不收敛——这就是调参。改一个数字、看结果怎么变,是最直观的"超参数"体验。
本节小结while 循环 = 训练迭代:初值是参数初始化,条件是"误差还不够小",更新是梯度下降。漏掉更新语句就是死循环;训练里忘了更新参数,模型永远学不会——一模一样。
配套视频 【梯度下降】3D 可视化讲解通俗易懂 B站 · 12:09 · 29.6 万播放 · 看"w 一轮轮被调整"的直观过程

AI-3 for = 遍历数据集 + 统计准确率

模型评测的骨架只有三行:for 遍历一条条数据 → 累加器统计对的个数 → 除以总数

10 条预测,算准确率
pred = [1, 0, 1, 1, 0, 1, 1, 1, 0, 1]   # 模型的预测(1 = 正类)
gold = [1, 0, 1, 0, 0, 1, 1, 1, 0, 1]   # 真实标签

correct = 0
for p, g in zip(pred, gold):            # 遍历一条条数据 = 一个 epoch
    if p == g:
        correct += 1                    # 累加器

accuracy = correct / len(gold)
print("正确率:%d/%d = %.1f%%" % (correct, len(gold), accuracy * 100))
运行结果(实测)
正确率:9/10 = 90.0%
本节小结for 遍历一遍数据 = 一个 epoch;累加器 + 除法 = 准确率。后面所有机器学习评测代码的骨架就是这三行——你已经会写了。
配套视频 黑马程序员 · 第 39 讲 案例:求 1—100 的和 B站 · 累加器写法与本节完全一致

AI-4 range 的步长 = 分批(batch)

数据太多,一次喂不进模型,所以要分批。分批最朴素的写法就是给 range() 一个步长。

20 条数据,每批 4 条
n = 20            # 共 20 条数据
batch_size = 4    # 每批处理 4 条

for start in range(0, n, batch_size):     # 步长就是批大小
    end = min(start + batch_size, n)      # 最后一批可能不满
    print("本批处理:第", start, "到", end - 1, "条")

print("一共分了", n // batch_size, "批")
运行结果(实测)
本批处理:第 0 到 3 条
本批处理:第 4 到 7 条
本批处理:第 8 到 11 条
本批处理:第 12 到 15 条
本批处理:第 16 到 19 条
一共分了 5 批
本节小结range(0, n, batch_size) 就是最朴素的分批写法。步长越大,每批数据越多、更新次数越少——这就是显存和训练速度的取舍。
配套视频 黑马程序员 · 第 45 讲 range 语句 B站 · 重点看第三个参数(步长)

AI-5 break = 早停(Early Stopping)

验证集准确率连续几轮不涨了就停——再练只会过拟合,还白烧电费。代码上就是一句 break

连续 3 轮没有进步就提前结束(名义上最多 100 轮)
x = 2.0
y_true = 10.0
w = 1.0
lr = 0.1
patience = 3             # 容许"连续几轮没有进步"
best_loss = 999
no_improve = 0

for epoch in range(1, 101):               # 名义上最多训练 100 轮
    pred = w * x
    loss = abs(pred - y_true)

    if epoch <= 6:                        # 前 6 轮正常学习
        w = w - lr * (pred - y_true) * x
    # 第 7 轮起模型"学不动了"(真实训练里常见:学习率衰减到 0、
    # 数据重复没有新信息、或已经到精度极限)

    if loss < best_loss - 1e-6:           # 比历史最好还低 → 有进步
        best_loss = loss
        no_improve = 0
    else:                                 # 没进步
        no_improve += 1

    print("第 %2d 轮:loss = %.6f,连续 %d 轮没进步"
          % (epoch, loss, no_improve))

    if no_improve >= patience:
        print(">>> 连续 %d 轮没有进步,触发早停!" % patience)
        break                             # 关键:提前跳出循环

print("实际只训练了 %d 轮,而不是 100 轮" % epoch)
运行结果(实测)
第  1 轮:loss = 8.000000,连续 0 轮没进步
第  2 轮:loss = 4.800000,连续 0 轮没进步
第  3 轮:loss = 2.880000,连续 0 轮没进步
第  4 轮:loss = 1.728000,连续 0 轮没进步
第  5 轮:loss = 1.036800,连续 0 轮没进步
第  6 轮:loss = 0.622080,连续 0 轮没进步
第  7 轮:loss = 0.373248,连续 0 轮没进步
第  8 轮:loss = 0.373248,连续 1 轮没进步
第  9 轮:loss = 0.373248,连续 2 轮没进步
第 10 轮:loss = 0.373248,连续 3 轮没进步
>>> 连续 3 轮没有进步,触发早停!
实际只训练了 10 轮,而不是 100 轮
本节小结"不再进步就停"就是 break。AI 训练里的 Early Stopping 和循环里的提前退出是同一个动作——省时间,也防止过拟合。
配套视频 黑马程序员 · 第 49 讲 continue 和 break B站 · break 的语法基础

AI-6 continue = 跳过脏数据

真实数据里总有缺失值、异常值。数据清洗的最小单元就是一行 continue:不合格的数据跳过,不计入统计。

8 条成绩里混了 3 条脏数据,只统计有效的 5 条
scores = [88, 95, -1, 76, None, 92, 103, 85]
total = 0
count = 0

for s in scores:
    if s is None or s < 0 or s > 100:     # 脏数据判定
        continue                           # 跳过,不计入统计
    total += s
    count += 1

print("原始数据", len(scores), "条,有效", count, "条")
print("有效成绩平均分:", round(total / count, 2))
运行结果(实测)
原始数据 8 条,有效 5 条
有效成绩平均分: 87.2
本节小结continue 是数据清洗的最小单元。第 8 章 Pandas 的 dropna()、爬虫的异常兜底,底层逻辑都是"不合格就跳过这一条"。
配套视频 黑马程序员 · 第 49 讲 continue 和 break B站 · continue 的语法基础

AI-7 循环 else = "这一轮完整跑完了吗"

训练日志里"共 N 轮,全部完成"和"在第 k 轮被中断"是两种完全不同的含义——正是 else 子句要表达的。

两种情况对比:跑满 3 轮 vs 第 2 轮被中断
print("情况一:正常跑完 3 轮")
for epoch in range(1, 4):
    print("  epoch", epoch, ":loss =", round(1.0 / epoch, 4))
else:
    print("  === 训练完整完成(未早停)===")

print("情况二:第 2 轮触发早停")
for epoch in range(1, 4):
    print("  epoch", epoch, ":loss =", round(1.0 / epoch, 4))
    if epoch == 2:
        print("  >>> 触发早停!")
        break
else:
    print("  === 训练完整完成(未早停)===")
运行结果(实测)
情况一:正常跑完 3 轮
  epoch 1 :loss = 1.0
  epoch 2 :loss = 0.5
  epoch 3 :loss = 0.3333
  === 训练完整完成(未早停)===
情况二:第 2 轮触发早停
  epoch 1 :loss = 1.0
  epoch 2 :loss = 0.5
  >>> 触发早停!
本节小结else 子句是 Python 专门用来标记"循环有没有被提前打断"的语法糖:自然结束才走 else,被 break 打断就不走。做"是否完整跑完"的判断时,它比设标志变量更清爽。

AI-8 嵌套循环 = 注意力矩阵的平方代价

一句话 27 个字 → 每个字都要和所有字算一遍相关度 → 27 × 27 = 729 次;字数翻倍到 54 → 54 × 54 = 2916 次(4 倍)。这就是长文本又慢又贵、按 token 收费的根因。

3 个词、每个词 4 维:手算注意力矩阵
vectors = [
    [1, 0, 0, 1],   # 词 1
    [0, 1, 1, 0],   # 词 2
    [1, 1, 0, 0],   # 词 3
]
names = ["我", "爱", "你"]

print("注意力矩阵(每个词对每个词的相关度):")
print("      " + "".join("%6s" % n for n in names))

count = 0
for i in range(len(vectors)):             # 外层:每个词
    row = []
    for j in range(len(vectors)):         # 内层:和每个词算一次
        score = 0
        for k in range(len(vectors[i])):  # 最内层:点积
            score += vectors[i][k] * vectors[j][k]
        row.append(score)
        count += 1
    print("  %2s  " % names[i] + "".join("%6d" % v for v in row))

print("总共执行了 %d 次点积 = 3 x 3" % count)
print("三层嵌套一共 %d 次乘法 = 3 x 3 x 4" % (3 * 3 * 4))
运行结果(实测)
注意力矩阵(每个词对每个词的相关度):
           我     爱     你
   我       2     0     1
   爱       0     2     1
   你       1     1     2
总共执行了 9 次点积 = 3 x 3
三层嵌套一共 36 次乘法 = 3 x 3 x 4
本节小结嵌套循环 = 每个元素都要和所有元素算一遍,计算量按平方增长。Transformer 的注意力矩阵就是这么算出来的——这就是"长文本贵"的数学原因。
配套视频 从词向量到 Transformer:AI 大模型背后的原理 B站 · 动画讲解 · 帮助理解"注意力"在算什么

交互演示区

改参数、点按钮,立刻看结果 · 浏览器内 JS 模拟(已与 Python 运行结果逐一对齐)

演示 1:成绩等级判定(多分支)

点"判定":先试 85,再试试 101、-1、80、79、60、59。

演示 2:BMI 判定(单边比较链)

试试 70 / 1.70(结果是"过重"),再试 55 / 1.65。

演示 3:素数判断(for-else)

试 17(素数)、91(= 7 × 13)、97、1。

演示 4:九九乘法表(嵌套循环)

把 9 改成 3 看看前 3 行长什么样。

演示 5:while 训练迭代(AI-2)

默认参数会在 14 轮后收敛到 w ≈ 5.0。把学习率改成 0.05 试试,轮数会明显变多。
这 5 个面板的逻辑与页面上对应的 Python 代码完全一致,只是用 JavaScript 重写以便在网页上直接运行;每一段的输出都与 Python 实跑结果对过账。

知识点总结卡 · 易错清单 · 作业

Summary · 学生速查 / 可打印

选择结构总结卡

结构写法何时用一句话
单分支if 条件:满足才做,不满足就算了一条路,可选
双分支if 条件: … else: …二选一else 后面不写条件
多分支if / elif / else三选一以上条件从严到宽,命中即停
嵌套if 里套 if判断有先后依赖能 elif 就别嵌套
三元a if 条件 else b简单二选一赋值只装得下一行才用

循环结构总结卡

结构写法何时用一句话
whilewhile 条件:次数未知初值 / 条件 / 更新,缺一不可
forfor i in 序列:次数已知、遍历计数交给 Python,不会死循环
rangerange(start, stop, step)生成整数序列含头不含尾,步长默认 1
break满足条件即跳出提前结束多重循环只跳一层
continue跳过本轮剩余跳过个别数据while 里别跳过递增语句
else挂在循环后"跑完了吗"有 break 就没 else
嵌套循环里套循环图形、矩阵总次数 = n × m

本章语法 ↔ AI 概念对照卡

本章语法AI 里的对应物本页演示
if / elif / else决策树的节点与路径AI-1
嵌套 if随机森林里的多棵树AI-1
while训练迭代(epoch 循环)AI-2
for遍历数据集(一个 epoch)AI-3
累加器 + 除法准确率 accuracyAI-3
range 的步长批大小 batch sizeAI-4
breakEarly Stopping 早停AI-5
continue跳过脏数据(dropna)AI-6
循环 else"训练是否完整跑完"AI-7
嵌套循环注意力矩阵(计算量平方增长)AI-8
猜数字的"猜—反馈—修正"RLHF 人类反馈强化学习§3.3

易错清单(巡机速查)

编号症状原因一句话诊断
E-01IndentationError缩进不一致空格和 Tab 混用了
E-02逻辑悄悄错了,不报错缩进层级写偏看 print 应该在第几层
E-03SyntaxError 指向 elseelse 后面写了条件else 不跟条件
E-04高分被判低等级elif 顺序从宽到严严格条件要放前面
E-05程序停不下来循环体里没更新循环变量补上 i = i + 1
E-06累加结果只剩最后一个数total = 0 写进了循环体清零要放在循环外
E-07range 少跑一次忘了 stop 取不到数到 n 就写 n+1
E-08ValueError: range() arg 3 must not be zero步长写了 0步长不能为 0
E-09break 只跳出一层只作用于所属层需要多层退出用标志变量
E-10continue 后死循环while 里跳过了递增语句递增写在 continue 之前
E-11else 子句不执行循环里执行了 break有 break 就没 else
E-12还没分等级就报错= 写成 ==,或括号没配平看报错指的那一行

课后作业

必做(下次课课前检查)

  1. 求 1—100 之间 5 的倍数的累加和(答案应为 1050,用两种写法各做一遍)。
  2. 求个人所得税(简化三档,单位万元),至少测试 3.6、14.4 两个边界值。

加分题

  • 把本章任意一个例子改成 while True + break 的写法,并说明改了什么、为什么。

课堂自测(8 题 · 5 分钟)

  1. range(1, 10) 产生几个数?
  2. list(range(0, 5, 2)) 的结果是什么?
  3. while 的三要素是什么?
  4. breakcontinue 的区别是什么?
  5. 循环的 else 什么时候执行?
  6. for i in range(3): for j in range(4) 一共循环几次?
  7. 成绩 85 分,按"先判断 ≥60 再判断 ≥80"的顺序写,结果是什么?
  8. 1—100 的奇数和是多少?
查看 8 题答案
  1. 9 个(1 到 9,stop 取不到)
  2. [0, 2, 4]
  3. 初值、条件、更新
  4. break 结束整个循环;continue 只跳过本轮
  5. 循环自然结束(没有遇到 break)时执行
  6. 12 次(3 × 4)
  7. B(错的,应该是 A —— 宽条件在前,85 ≥ 60 先命中)
  8. 2500

备课提醒:本讲课件(PPT)需要注意的几处

  • S01 目录页写的是第 2 章内容,讲课时跳过,用 S03/S04 开场。
  • S04 与 S16 有两套章节编号(S04:3.1 顺序 / 3.2 选择 / 3.3 循环;S16:3.1 选择 / 3.2 循环),以 S04 为准。
  • S10 的三角形题要求"重复执行 5 次",但循环到 S17 才讲——建议移到上机一,本页已按此安排。
  • 例题编号有重复:S10 与 S37 都叫例 3-3,S24 与 S28 都叫例 3-7,讲课时说清楚"两个 3-3"。S23 是空页,直接跳过。
  • S29 / S34 的代码里混入了中文全角引号(如 print(i,end=’,’)),复制到 IDLE 会直接 SyntaxError——让学生手打,不要复制。
  • S42 的个税题:"调佣函数"应为"调用函数";单位是万元但没标注;且真实个税是七级超额累进,这里是简化三档,不要拿去真算工资。
学时提示:本章共 21 个知识点 + 9 个综合案例,按"讲授 2 学时 + 上机 4 学时"排,讲授环节大约缺 1 学时。建议把"顺序结构 + 单分支 + 三元表达式"下放为自学 + 上机验证,或把综合应用只精讲"猜数字四连 + 登录系统"两例,其余留作作业。

下讲预告

第 4 次课 · 第 4 章 组合数据结构
列表 · 元组 · 字符串 · 集合 · 字典 · 序列的切片与通用操作
回到课程主页查看课次 →