跳到主要内容
Python 程序设计 · 在线教材适用于 2026—2027 学年第一学期

第 3 章 · 让程序学会作决定

分支与报名判定

第二章的程序会接收和计算数据,却不会根据规则选择结果。本章把“名额、身份、费用”的业务要求翻译成布尔条件与分支路径,让每一次报名都有唯一、可解释的判定。

预计阅读
60 分钟
上机实践
90 分钟
前置知识
第 1—2 章
本章产出
个人报名判定器

学完本章,你应该能够

  • 读懂 True、False 和六个比较运算符,区分赋值 = 与比较 ==。
  • 用 if、elif、else 和正确缩进表达互斥规则。
  • 用 and、or、not 组合清楚的条件。
  • 先画决策表,再写报名判定程序,并解释条件顺序为何重要。
  • 用四条路径和边界值核验结果,而不只检查一次“运行成功”。

3.1 从“会计算”走向“会判断”

第 2 章的预算计算器不论输入哪项活动,都会沿同一条路线完成计算。ClubHub 的报名流程不同:名额已满时要拒绝;有名额但不是本校学生,也要拒绝;前两项通过而费用未付足,仍要拒绝。只有三项都通过,才能成功报名。

把问题写成“输入—处理—输出”(IPO),可以看出本章新增的步骤是选择路径,而不是增加一条算式。

阶段报名判定器要做什么
输入姓名、活动总名额、已报名人数、校内身份、应付金额、已付金额
处理按“名额 → 身份 → 费用”逐项判断;成功时人数加 1,再算剩余名额
输出姓名和唯一的主要结果:三种失败原因之一,或成功及剩余名额
先定业务口径,再写代码

如果一位申请者既遇到满额又未付足费用,本章约定先报告“名额已满”。这不是 Python 自动决定的;它是我们先写进规则、再用分支顺序实现的业务优先级。

3.2 布尔值与比较表达式

“当前人数是否已经达到容量?”是一个能回答真或假的问题。Python 用布尔值 True 和 False 表示这两种结果;它们首字母大写,不加引号。写成 "True" 就是普通字符串,不是布尔值。

boolean_check.py
capacity = 30
registered = 29
print(registered < capacity)
print(registered >= capacity)
print(type(registered < capacity))
运行结果
True
False
<class 'bool'>

第一项提问“还有没有名额”,第二项提问“是否满额”。同一组数据让前者为真、后者为假。type() 显示比较结果属于 bool 类型。

运算符意思报名情境
==等于campus_status == "y"
!=不等于campus_status != "y"
>大于registered > capacity
>=大于等于registered >= capacity
<小于paid_amount < activity_fee
<=小于等于registered <= capacity

一个等号做动作,两个等号问问题

assign_or_compare.py
capacity = 30
print(capacity == 30)
capacity = 31
print(capacity == 30)
运行结果
True
False

capacity = 30 把右侧的值关联给名称;capacity == 30 不改变变量,只产生布尔结果。在 if 条件中误写 if campus_status = "y": 会产生语法错误。

3.3 用边界选对比较符

容量为 30 时,29 人尚有空位,30 人恰好满额,31 人虽然提示数据可能有问题,也不能继续放行。业务语言里的“已经满额”因此要写作 registered >= capacity,不能只写 ==。

capacity_boundary.py
capacity = 30
registered = 29
print(registered >= capacity)
registered = 30
print(registered >= capacity)
registered = 31
print(registered >= capacity)
运行结果
False
True
True
业务说法正确表达式边界上的结果
仍有名额registered < capacity30 / 30 为假
已经满额registered >= capacity30 / 30 为真
至少支付 20 元paid_amount >= 20支付 20 元为真
费用不足paid_amount < activity_fee19.99 / 20 为真

不要凭感觉选符号。先把规则切换的点写出来,再分别测试边界下方、恰好边界、边界上方。课堂课件中的 29、30、31 和 19.99、20、20.01 正是这样的样本。

3.4 if 与缩进:真时才执行

if 后跟一个条件,末尾要有英文冒号。条件为 True 时,执行下方缩进的代码块;为 False 时跳过它。缩进是 Python 识别代码块的语法,不是美化排版。

first_if.py
registered = 30
capacity = 30
if registered >= capacity:
    print("报名失败:活动名额已满")
print("判定结束")
运行结果
报名失败:活动名额已满
判定结束

把 registered 改成 29 再运行,只会输出“判定结束”。它与 if 对齐,在分支之外,因此两种情况都会执行。若误把最后一行也缩进,就会产生“只有满额时才显示判定结束”的逻辑错误;若 if 后一行完全不缩进,则会出现 IndentationError。

书写约定

本课程统一在同一层级使用 4 个空格缩进。冒号、括号与引号都使用英文符号。编辑器通常会在按回车后自动缩进;仍要自己检查对齐层级。

3.5 if-else:两条互斥路径

如果希望条件为真与为假时分别做事,就在 if 后加 else。同一组 if-else 只走其中一条路径,不会两条都走。

two_paths.py
registered = 29
capacity = 30
if registered >= capacity:
    print("报名失败:活动名额已满")
else:
    print("还有名额,继续检查身份和费用")
运行结果
还有名额,继续检查身份和费用

这里的 else 只表示“未满额”,不代表已经可以报名成功。如果此时就输出成功,将跳过身份与费用规则。正确做法是让后续条件继续检查,全部通过后才更新人数。

3.6 elif:顺序就是优先级

报名有三种不同的失败原因,一组 if-elif-else 可以从上到下逐项排除。命中第一条为真的路径后,该组后面的 elif 与 else 都不再执行;因此本章能保证只给出一个主要结果。

first_matching_rule.py
registered = 30
capacity = 30
campus_status = "n"
activity_fee = 20.0
paid_amount = 0.0

if registered >= capacity:
    print("名额已满")
elif campus_status != "y":
    print("身份不符")
elif paid_amount < activity_fee:
    print("费用不足")
else:
    print("报名成功")
运行结果
名额已满

这组输入同时违反三条规则,却只显示“名额已满”,因为它是约定的首要原因。若把“有名额就成功”写在第一条,29 人、身份为 n 的申请者会被错误放行。三个彼此独立的 if 也不适合这里:它们可能连续输出多条失败信息。

先排除失败:满额 → 非本校 → 费用不足 → 其余情况才成功

else 不带条件,表示前面所有失败条件都没有命中。它是本项目成功路径最清楚的位置。

3.7 用 and、or、not 组合条件

一个判断有时由多个问题组成。and 要求两边都为真;or 只要至少一边为真;not 把真假反过来。先把每个判断存入有业务含义的变量,再组合,通常比一整行长条件更容易读。

combined_conditions.py
capacity = 30
registered = 29
campus_status = "y"
activity_fee = 20.0
paid_amount = 20.0

has_place = registered < capacity
is_student = campus_status == "y"
fee_ok = paid_amount >= activity_fee
can_register = has_place and is_student and fee_ok
print(has_place, is_student, fee_ok)
print(can_register)
print(not has_place)
运行结果
True True True
True
False

这里三项均为真,can_register 才为真;not has_place 表示“没有空位”,所以为假。若允许本校学生或教师,身份规则可以写成下面这样;这是进阶变体,不是本章基础项目的规则。

identity_extension.py · 进阶
campus_status = "teacher"
identity_ok = campus_status == "y" or campus_status == "teacher"
print(identity_ok)
运行结果
True

在这些布尔条件中,比较先于 not,然后是 and、or。混合多种运算时,用括号或中间变量明确意图。Python 的 and 和 or 还会短路求值:当前一部分已足以确定结果时,后一部分可能不再计算;本章先以条件清晰、可解释为目标。

组合条件与多分支各司其职

can_register 很适合回答“能不能”;报名系统还要回答“为什么不能”,所以完整项目仍使用按优先级排列的多分支。

3.8 编码前先写规则决策表

决策表把输入情形、首个命中的条件和预期结果放在一起。先写预期、再运行程序,才能真正检验规则;如果只看程序跑出了什么,再把它抄作“预期”,错误也会被当成正确。

用例已报名 / 容量身份已付 / 应付首条命中路径预期
A30 / 30y20 / 20满额名额已满
B29 / 30n20 / 20身份仅限本校学生
C29 / 30y19.99 / 20费用活动费用未付足
D29 / 30y20 / 20成功报名成功,剩余 0

A 验证恰好满额,C 与 D 夹住“付足 20 元”的边界,D 同时验证成功后的人数更新。进一步让已报名人数为 31,应仍进入满额路径。若多个失败条件同时成立,按表中规则仍只显示优先级最高的原因。

3.9 完成活动报名判定器

现在把第 2 章学过的输入与类型转换,和本章的比较、分支组合起来。保存为 registration_judge.py,一次运行只处理一名申请者。请先预测用例 D 的结果,再复制代码运行。

registration_judge.py
# 第3章:一次处理一名申请者
# 规则优先级:名额 → 身份 → 费用 → 成功
student_name = input("请输入姓名:")
capacity = int(input("请输入活动总名额:"))
registered = int(input("请输入当前已报名人数:"))
campus_status = input("是否为本校学生(y/n):")
activity_fee = float(input("请输入应付活动费:"))
paid_amount = float(input("请输入已支付金额:"))

if registered >= capacity:
    result = "报名失败:活动名额已满"
elif campus_status != "y":
    result = "报名失败:本活动仅限本校学生"
elif paid_amount < activity_fee:
    result = "报名失败:活动费用未付足"
else:
    registered = registered + 1
    remaining_places = capacity - registered
    result = f"报名成功,剩余名额:{remaining_places}"

print()
print(f"学生:{student_name}")
print(result)
运行结果
请输入姓名:李明
请输入活动总名额:30
请输入当前已报名人数:29
是否为本校学生(y/n):y
请输入应付活动费:20
请输入已支付金额:20

学生:李明
报名成功,剩余名额:0

上方运行结果中的“李明、30、29、y、20、20”是示例键盘输入,不是源代码。在线运行时,在运行器中按提示逐项输入;复制按钮只复制上方的程序。

  1. 1
    输入六项数据

    人数用 int(),金额用 float()。campus_status 暂时约定为小写 y 或 n。

  2. 2
    先排除失败

    依次检查满额、身份和费用。每条失败路径给出不同的解释。

  3. 3
    成功后更新状态

    只有走进 else 才让 registered 加 1;随后用新人数计算剩余名额。

  4. 4
    统一输出

    最后只打印一次姓名和结果,避免多个分支重复打印或同时报出几个原因。

当前程序的已知边界

它一次只处理一人;大写 Y 不等于小写 y;非数字输入可能使转换失败;人数或金额的异常值尚未单独校验。这些不是“已经支持”的功能,也不要用循环、异常处理或 match-case 偷换本章练习目标。

3.10 用四条路径和边界值排错

把上一节的程序从头运行四次,逐项输入决策表的 A—D。姓名可以自选,但每次都要在运行前记录“预期”,运行后记录“实际”。四次都符合,才说明四条核心路径得到覆盖。

测试重点观察如果结果不对,先检查
A · 满额30 / 30 不成功;31 / 30 也不成功>= 是否误写成 > 或 ==
B · 身份有名额但 n 被拒绝是否过早把“有名额”当作成功
C · 费用19.99 / 20 被拒绝< 方向与金额变量是否写反
D · 成功20 / 20 通过,29 加 1 后剩余 0成功更新是否在 else 内、计算顺序是否正确

错误不止一种

漏英文冒号、if 后没有缩进,属于语法或缩进错误,程序不能正常开始;把最后的输出误缩进进分支,程序可能能运行,但路径不符合预期;把“至少支付”写成 >,则会在恰好付足时出现边界错误。语法报错时读报错行附近;能运行但结果不对时,拿决策表追踪首个命中的条件。

固定排查顺序冒号→缩进→比较符→条件顺序→边界值复测

进阶观察:输入 Y 时,本版会进入“仅限本校学生”路径。它揭示了大小写处理尚未实现;第 5 章学习字符串清洗后再改。输入汉字金额会触发转换错误,异常处理留到后续章节。

3.11 练习与个人任务

先预测,再看答案

  1. 读布尔结果

    capacity = 30、registered = 30 时,registered < capacity 与 registered >= capacity 各是什么?

    查看答案

    依次为 False 与 True。恰好 30 人已无空位。

  2. 判断路径

    29 / 30、身份 n、已付 0 / 应付 20,应该输出哪一个原因?为什么不是两个?

    查看答案

    输出“本活动仅限本校学生”。名额尚有空位,第二条身份规则首先命中;同一组分支不会继续执行费用路径。

  3. 修正比较符

    “支付金额至少 20 元才能通过”应使用 > 还是 >=?测试哪三个金额?

    查看答案

    使用 >=;可测 19.99、20、20.01,分别位于边界下方、边界点和边界上方。

  4. 追踪状态

    29 / 30 的申请者成功时,为什么剩余名额是 0 而不是 1?

    查看答案

    成功后先令 registered = registered + 1,新人数为 30,再计算 30 - 30 = 0。

第 3 周 · 个人机房任务

独立完成活动报名判定器

每位学生独立编写、测试并解释自己的程序。同伴可以临时互测、提供反馈,但源文件、决策表、测试记录和讲解均由本人完成与提交。

最低要求

  • 程序接收姓名、容量、已报名人数、校内身份、应付与已付金额六项输入。
  • 按“满额 → 身份 → 费用 → 成功”输出一个明确结果;成功时更新人数并显示剩余名额。
  • 提交规则决策表和四条路径的“输入—预期—实际—结论”,另测 31 / 30 的边界。
  • 提交一张成功路径、一张失败路径的截图,附 50—100 字错误复盘。
  • 源文件命名为 w03_学号_registration_judge.py;课堂展示时能解释某组输入为何走到该分支。
下载完整示例代码

基础四路径通过后,可任选进阶:允许教师身份,或新增 registered > capacity 的“数据异常”提示。先扩展规则表和测试,再修改代码;这些不是基础任务的必做项。

章末检查

  • 我能解释 =、== 和 != 的不同作用。
  • 我知道冒号和缩进决定哪个语句属于哪个分支。
  • 我能说明为什么报名项目把成功留在最后的 else。
  • 我在运行之前写过四条路径的预期,并测过等号附近的边界。

延伸阅读:廖雪峰《Python教程》在线 PDF中的“数据类型和变量”“条件判断”,以及菜鸟教程 Python 3 条件控制。本章按 ClubHub 报名规则重组示例,并以课程主数据的知识边界为准。