打开课程目录
Module 6预计 100 分钟

作用域、程序拆分与函数测试

让每个函数负责一件事,并用多组输入检查边界

学习目标

理解 local variable 和基础 scope;能够跟随示例说明 helper functions 如何拆分任务,并理解为什么要用多组输入和边界值检查返回结果。

开始之前

会定义函数只是开始。可靠的函数还需要清楚的数据边界:哪些变量只在函数内部使用、函数负责哪一个小任务,以及如何证明它对不同输入都有效。

本课把 M3 的边界检查、M4 的循环与累加、M5 的 list 和 dictionary 放进函数中。目标不是写更长的代码,而是把复杂度分成几个可以单独理解和测试的部分。

概念 01

Local variable 通常只在自己的函数内部可用

函数内部的数据不随意散落到程序其他位置,可以减少变量名互相干扰,也让数据从哪里来更清楚。

Local variable 的英文全称是 Local variable,中文叫局部变量,Python 中没有额外关键字;它是在函数内部创建的普通变量。例如 def calculate(): 内的 total = 10 + 5 会创建 local variable total。它的实际用途是在函数内部保存中间结果。

Scope 的英文全称是 Scope,中文叫作用域,描述一个变量可以在哪里使用。函数内部创建的 local variable 通常属于这个函数的 scope;在函数外直接 print(total) 会产生 NameError,因为外部位置找不到内部的 total。

下面的 message = "Hello" 只在 example() 内部可用。调试时看 NameError 的行号,再问:这个变量在哪一行创建?当前使用位置是否在同一个函数内部?本课不使用 global,只通过 parameter 把数据传入、通过 return 把结果传出。

  • Local variable 在函数内部创建
  • Scope 描述变量可使用的位置
  • 函数外不能直接读取内部变量
  • NameError 时检查创建与使用位置
  • parameter 进入、return value 出来

在函数外读取 local variable 会产生 NameError

message 在 example 的函数 scope 中创建;调用结束后,函数外的 print(message) 不能直接找到它。需要使用时应 return message。

Python
代码编辑器将在滚动到这里时加载
点击运行时准备 Python
TERMINAL程序输出

点击“运行”后,程序输出会显示在这里。

预期结果:NameError: name 'message' is not defined(错误位于 print(message))

概念 02

Decomposition 把大问题拆成职责明确的小函数

读取输入、判断、计算和输出全部挤在一个函数中会很难检查;小函数可以分别理解、测试和复用。

Decomposition 的英文全称是 Decomposition,中文叫程序拆分,表示把较大的问题拆成几个较小任务。Python 没有叫 decomposition 的关键字;实现方式是定义职责清楚的 functions,例如成绩程序拆成 is_passing(score)、calculate_average(scores) 和 show_result(...). 它的实际用途是降低每一部分的认知负担。

Helper function 的英文全称是 Helper function,中文叫辅助函数,是帮助另一个较大任务完成一个小步骤的函数。例如 def is_passing(score): return score >= 50 只负责判断;show_status(score) 调用它并负责显示。

小而明确不代表每行都建函数。一个函数应该能用一句话描述职责,并通过 parameter 接收所需数据、return 需要交出的结果。Helper function 不应偷偷依赖函数外的临时变量。

  • Decomposition 把大问题拆成小任务
  • 每个函数负责一件清楚的事
  • Helper function 帮助完成一个小步骤
  • 数据通过 parameter 进入
  • 结果通过 return 交出

判断与显示由两个函数分别负责

is_passing 只产生 Boolean 判断结果;show_status 使用这个 return value 决定显示哪条信息。

Python
代码编辑器将在滚动到这里时加载
点击运行时准备 Python
TERMINAL程序输出

点击“运行”后,程序输出会显示在这里。

预期结果:Pass / Try again

概念 03

Function testing 使用多组输入,也要检查缺少 return

一个输入正确不能证明函数普遍正确;边界值和不同值能暴露比较、循环或返回路径中的错误。

Function testing 的英文全称是 Function testing,中文叫函数测试,Python 中通过多次 function call 完成,例如 is_passing(49)、is_passing(50)、is_passing(80)。实际用途是分别检查普通值、boundary value 和不同值,而不是只选一个容易通过的输入。

对 is_passing(score) 来说,50 是规则改变的 boundary value:49 应为 False,50 和 80 应为 True。测试时先写下预期 return value,再运行调用并比较实际结果,这延续了 M3 的边界检查方法。

Missing return 的英文可以理解为“缺少 return”,本课把它作为调试技能。def add_bonus(score): score + 5 只是计算后丢弃结果;写成 print(score + 5) 也只显示。使用 result = add_bonus(80) 和 print(result) 会看到 None,提醒你检查函数是否把结果交回。

  • 至少测试多个不同输入
  • Boundary value 检查规则切换点
  • 先写预期再比较实际
  • 只有计算表达式不会自动返回
  • 出现 None 时检查 return 路径

用边界值测试函数

49 检查边界下方,50 检查边界本身,80 是另一个普通值;三组结果一起验证 comparison。

Python
代码编辑器将在滚动到这里时加载
点击运行时准备 Python
TERMINAL程序输出

点击“运行”后,程序输出会显示在这里。

预期结果:False / True / True

概念 04

让 helper function、列表遍历和返回值逐层协作

把旧知识放进职责明确的函数后,可以建立“数据进入 → 小任务协作 → 结果返回 → 动态输出”的完整程序链。

先单独看 is_passing(score):parameter 接收一个分数,comparison 产生 Boolean,return 交回结果。再看 count_passes(scores):parameter 接收 list,Accumulator passed 从 0 开始,for 逐项遍历,if 使用 helper function 的 return value 决定是否累加。

最后,scores 保存实际数据,count_passes(scores) 的 return value 交给 result,f-string 才负责显示。每层只有一个清楚任务,因此可以先测试 is_passing 的 49、50,再测试 count_passes 的普通列表和空列表。

空 list 是重要 boundary case:for 不会执行任何一轮,passed 保持 0,函数仍然 return 0。这说明好的初始值能让循环自然处理没有元素的情况。

  • 先理解并测试 helper function
  • 主函数遍历 list
  • if 使用 helper 的 Boolean 返回值
  • Accumulator 保存累计结果
  • 空列表自然返回 0

逐层组合判断、循环、累加与输出

helper function 判断单个分数;主函数遍历列表并累加;最终 return value 保存到 result,再由 f-string 显示。

Python
代码编辑器将在滚动到这里时加载
点击运行时准备 Python
TERMINAL程序输出

点击“运行”后,程序输出会显示在这里。

预期结果:Passed: 2

常见错误

这些写法为什么不工作

错误不是失败,而是 Python 告诉你“这部分我无法理解”。先找到差异,再一次只改一个问题。

函数完成计算,却没有交回结果

表达式本身不会自动成为 return value。把调用结果保存后输出,若看到 None,就检查函数是否在需要的路径执行 return。

def calculate_area(width, height):
    width * height

area = calculate_area(4, 3)
print(area)
改成
def calculate_area(width, height):
    return width * height

area = calculate_area(4, 3)
print(area)

只测试一个远离边界的值

80 通过不能证明 50 的边界写对。至少加入边界下方、边界本身和另一个不同值。

def is_passing(score):
    return score > 50

print(is_passing(80))
改成
def is_passing(score):
    return score >= 50

print(is_passing(49))
print(is_passing(50))
print(is_passing(80))

引导修改

修改 helper function 并增加边界测试

这不是正式练习。跟着步骤修改已有代码,观察每次改变如何影响程序。

  1. 1先运行代码,确认 has_target(60) 的 return value 是 True。
  2. 2把 helper function 的规则改为 score 至少达到 70。
  3. 3增加对 69 和 70 的 function call,分别把 return value 保存到变量。
  4. 4使用 f-string 输出两个变量,确认 69 是 False、70 是 True。

完成后:修改后的 helper function 在 69 返回 False,在边界 70 返回 True,两个结果都保存并输出。

修改这段代码

先运行,再按左侧步骤逐项修改。你的草稿会保存在这个浏览器中。

Python
代码编辑器将在滚动到这里时加载
点击运行时准备 Python
TERMINAL程序输出

点击“运行”后,程序输出会显示在这里。

正式练习

现在独立完成 2 道练习

先运行代码观察问题,再点击“检查答案”。检查只关心题目要求的结果,不要求你照抄参考写法。

正式练习

让加分结果真正返回

练习 1/2

add_bonus(score) 应该使用 return 把新的分数交回调用位置,让 result 保存 85,并只输出 Result: 85。当前函数可能只是显示了计算结果。先 Run,观察 result 得到什么,再修复;保留 add_bonus function 和调用,不要直接把 result 固定写成 85。

8 分钟难度 2/3修复错误
代码编辑器将在滚动到这里时加载
TERMINAL程序输出

点击“运行”后,程序输出会显示在这里。

需要一点提示?

先自己尝试一次;卡住时再逐级打开提示。

本课总结

本课小结

  • 1Local variable 通常只在函数 scope 中使用,NameError 时检查创建位置与使用位置。
  • 2Decomposition 把较大问题拆成职责明确的 functions,helper function 负责一个小步骤。
  • 3Function testing 应覆盖普通值、boundary value、不同值与适用的空数据。
  • 4缺少 return 或只使用 print() 会让调用得到 None;完整程序通过 parameters 和 return values 连接。