学习前提
理解列表、循环与变量;会用表达式计算结果。
本课完成后你能做什么
把计算与输出分开,避免可变默认参数共享状态。
先理解机制
def定义函数,调用时才执行函数体。参数是输入,return是输出;没有返回值的路径会得到None。把格式化规则做成纯函数,比在多个地方复制print表达式更容易验证和修改。
位置参数适合简短明确的调用,关键字参数提高可读性。默认值在定义时求值,不会为每次调用重新创建。用空列表做默认参数容易让不同调用共享同一个列表;使用None作为哨兵并在函数内部创建容器。
局部变量通常只在当前函数作用域中可见。尽量通过返回值传递结果而不是修改全局变量;需要修改传入列表时,在函数名和约定中表达清楚。类型注解是说明与工具支持,不会自动执行运行时校验。
测试从需求出发:0分钟该怎样显示、负数是否拒绝。assert可用于练习中的内部检查,但生产输入校验应用明确异常或分支,因为优化执行可关闭断言。
可运行示例
使用Python 3.11或更新版本,将代码保存为独立练习目录中的lesson.py,在该目录执行 python3 lesson.py;Windows也可使用 py lesson.py。示例只使用标准库。
def duration_label(minutes):
if type(minutes) is not int or minutes < 0:
raise ValueError("minutes must be a nonnegative integer")
hours, rest = divmod(minutes, 60)
return f"{hours}h {rest}m"
def collect(title, items=None):
if items is None:
items = []
items.append(title)
return items
assert duration_label(125) == "2h 5m"
assert duration_label(0) == "0h 0m"
print(duration_label(125))
print(collect("Python"), collect("SQL"))
推演与验证
输出2h 5m,两个独立列表分别包含Python与SQL。把items默认值改为[]后多次调用,观察状态泄漏,再恢复。传入True会被当前严格整数规则拒绝,这一规则是有意的,因为bool在Python中属于int子类。
常见错误与边界
不要用捕获所有异常掩盖函数约定不清。默认参数和传入容器的所有权要写清楚:示例在调用者提供列表时会修改该列表。
动手练习
增加format_average(scores),空集合返回None,有数据时返回平均值。测试不修改原列表,并说明是否接受浮点分数。
参考解法与理由
把过滤或拒绝策略放在函数入口。返回数字由调用者决定展示精度,避免业务计算函数只能返回带装饰文字的字符串。
深入实验:用函数契约处理空数据与非法输入
学习时长平均值涉及三个决定:单位是什么、空列表返回什么、错误数据是否静默忽略。下面约定只接受非负整数分钟,空列表没有平均值,返回None;数据错误直接报错。这比把所有失败都返回0更容易区分“没有学习”与“没有数据”。
def average_minutes(values):
if any(type(value) is not int or value < 0 for value in values):
raise ValueError("minutes must be nonnegative integers")
if not values:
return None
return sum(values) / len(values)
print(average_minutes([]))
print(average_minutes([0, 30, 60]))
for bad in [[-1], [True], ["30"]]:
try:
average_minutes(bad)
except ValueError:
print("rejected")
assert average_minutes([0]) == 0
预期观察
输出None、30.0,然后三行rejected。True虽然是Python整数的子类,在这里也被拒绝,因为它不是时长记录。
原因、取舍与适用边界
使用type(value) is int是有意的业务限制,不是所有代码都应照搬的规则。当前函数要求可重复遍历、可求len的列表式输入;若传生成器,any会消耗它而len又不可用,需要先转成受大小限制的列表或改为单次迭代。计算与显示分开,None可以在界面显示“暂无记录”,保留一个没有副作用的计算函数会更容易测试。
进阶练习
如果新增需求允许小数分钟,直接删除类型校验有什么隐患?
解法与推理
字符串、布尔值、NaN和无穷值也可能进入计算。应重新定义数值类型和有限性约束,继续拒绝负值,并给空输入、零、小数、NaN各加一个验收案例;单位仍必须统一。