Python 生成器:按需清洗学习记录并理解一次性迭代
用生成器逐条清洗文本记录,观察 yield、next 与列表转换的消费过程,避免把已经耗尽的迭代器当成可反复使用的数据列表。
本节目标
用生成器逐条清洗文本记录,观察 yield、next 与列表转换的消费过程,避免把已经耗尽的迭代器当成可反复使用的数据列表。
知识与使用场景
如果一批数据只需要逐条处理,不一定先建立一个包含所有清洗结果的列表。生成器函数在运行到 yield 时交出一个值,并保留当前位置,下次请求时继续执行。
本节过滤空行和注释行,再把有效记录转成整数。它选择严格失败策略:格式错误直接抛异常,不悄悄丢弃;负数也会报告错误。输入是演示列表,但同样的迭代方式可以接收文件对象逐行提供的数据。
生成器自身不会缓存已经交出的值。next 取出第一条后,再用 list 收集,只能得到剩余部分。若后续工作需要反复遍历,应该重新创建生成器,或明确将全部结果保存成列表。
完整示例
def valid_minutes(lines):
for line in lines:
text = line.strip()
if not text or text.startswith("#"):
continue
value = int(text)
if value < 0:
raise ValueError("时长不能为负数")
yield value
source = [" 20 ", "", "# 休息日备注", "35", "0"]
stream = valid_minutes(source)
print(next(stream))
print(list(stream))
print(list(stream))
print(sum(valid_minutes(source)))预期结果
20
[35, 0]
[]
55运行与逐步理解
第一行 next 驱动函数开始处理,在遇到 20 时暂停。之后 list 继续请求值,空行和注释被跳过,得到 35 和 0。再次转换同一个 stream 时已经没有剩余元素,因此是空列表。
最后一行重新调用 valid_minutes,创建一个新的生成器,从头统计得到 55。若错误地对已经耗尽的 stream 求和,将得到 0。这类问题很容易隐藏在先打印调试、后进行计算的代码里。
延迟执行也意味着错误可能发生在消费阶段。仅把 stream = valid_minutes(source) 放在 try 中,通常捕获不到后面迭代时的格式错误;需要围住实际的 next、for 或 list 操作。
常见问题与边界
- 生成器没有列表那样的通用随机索引,不应直接使用 stream[0]。
- 如果输入本身已经是巨大列表,生成器只减少中间结果占用,不会让原列表消失。
- 空输入的 next 会抛 StopIteration;可以使用 next(stream, None) 给出默认值。
- 文件逐行生成器必须在文件仍打开时消费,不能把关闭后的文件留给后续读取。
动手练习
对空列表创建生成器,使用带默认值的 next;再把输入改为 ["20", "错误"],观察异常何时发生。
参考答案
next(valid_minutes([]), None) 返回 None。第二组第一次 next 返回 20,第二次请求结果时 int 转换才抛 ValueError。创建生成器对象这一行并没有提前完成数据校验。
文章回复
0 条公开回复