题目重点:解释yield创建和推进时机、单次消费与生成器内存,避免把惰性等同零存储。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练普通同步生成器,不混入async generator;消费者可能提前停止。
解题路线
- 含yield的函数调用会创建生成器对象,函数体不是立即全部执行;next或迭代才推进到下一yield并返回值,同时保存局部变量和执行位置。
- 生成器是单次消费的迭代器,耗尽后再次list通常为空;需要重复遍历时重新调用生成器函数,不能无意缓存同一个已耗尽对象。
- 惰性适合大日志或分页处理,减少完整结果列表存储。但生成器局部变量、缓存、底层连接以及yield的对象仍占内存;若内部先read全文件再yield并未解决全量占用。
- 消费者提前退出时,资源生命周期不能寄希望于某解释器立即垃圾回收。用明确context manager、close与try/finally清理;异常、取消和break路径都要测试。
- 发送数据send、异常throw或close会影响暂停点行为,但普通迭代仅靠next。把复杂双向生成器当普通列表可能隐藏控制流与错误路径。
- 测试首次调用是否触发副作用、部分消费、耗尽、重新创建、异常推进和清理。共享同一个生成器给多个并发消费者需要额外协作,不是天然线程安全队列。
手动走一遍
gen里先记录started再yield 1、yield 2;g=gen()后started仍空,next(g)才记录并返回1,list(g)只剩[2],再list(g)为空。重新gen()才能从头开始。
规模与取舍
输出n项至少O(n)时间;工作内存取决于保存状态与底层缓冲,简单计数生成器O(1),消费成list仍O(n)输出空间。
易错点与追问
容易失分的边界
- 创建生成器便执行全部函数体。
- 二次消费期望从头开始。
- 生成器内部全量读取却声称常量存储。
继续深挖
- 提前break怎样释放文件?
- yield from怎样传播结果和异常?
- async generator与同步有何生命周期差别?
技术参考
- Python Data Model:技术语义参考,具体题目按本文约定。
