这是一个非常深入且重要的 Python 核心概念组合。我们逐一详细解释:
生成器(Generator)与迭代器(Iterator)
迭代器(Iterator)
定义:迭代器是实现了 __iter__() 和 __next__() 方法的对象,用于逐个访问容器中的元素。
__iter__():返回迭代器对象本身。__next__():返回下一个元素,如果没有更多元素,则抛出StopIteration异常。
示例:
| |
Python 中很多内置类型(如 list、dict、set)都支持迭代,因为它们实现了 __iter__(),返回一个迭代器对象。
生成器(Generator)
定义:生成器是一种特殊的迭代器,使用 yield 关键字定义。它在每次调用 next() 时“暂停”并“恢复”函数状态,而不是一次性返回所有值。
优点:
- 节省内存:按需生成值,不一次性加载所有数据。
- 惰性求值(Lazy Evaluation):只在需要时计算。
示例:
| |
生成器表达式:
| |
生成器 vs 迭代器:
| 特性 | 生成器 | 迭代器 |
|---|---|---|
| 定义方式 | yield 或生成器表达式 | 类实现 __iter__ 和 __next__ |
| 易用性 | 更简洁 | 需手动实现协议 |
| 状态管理 | 自动保存函数局部状态 | 需手动管理状态(如索引) |
| 内存效率 | 高(惰性求值) | 取决于实现,可高可低 |
生成器是迭代器的一种实现方式,但不是所有迭代器都是生成器。
内存管理(Memory Management)
Python 使用自动内存管理,主要依赖:
- 引用计数(Reference Counting)
- 垃圾回收器(Garbage Collector, GC)
内存管理的目标是:自动分配和回收对象内存,避免内存泄漏和悬空指针。
引用计数(Reference Counting)
原理:
每个对象维护一个计数器,记录有多少个“引用”指向它。
- 引用增加:赋值、传参、放入容器等。
- 引用减少:变量离开作用域、被重新赋值、从容器中删除等。
- 当引用计数归零 → 对象被立即销毁,内存被回收。
示例:
| |
优点:
- 实时性:对象一旦无引用,立即回收。
- 简单高效。
缺点:
- 无法处理循环引用(Cycle Reference)
循环引用(Cycle Reference)
什么是循环引用?
两个或多个对象互相引用,导致它们的引用计数永不归零。
示例:
| |
后果:
内存泄漏 —— 对象无法被回收,占用内存持续增长。
GC 机制(Garbage Collector)
Python 使用 分代垃圾回收(Generational GC) 来解决循环引用问题。
基本原理:
- 跟踪所有容器对象(如 list、dict、class instance 等),因为只有它们能形成循环引用。
- 使用可达性分析:从“根对象”(如栈帧、全局变量)出发,标记所有可达对象,未被标记的就是垃圾。
- 定期运行回收算法(通常是标记-清除)。
分代回收(Generational Collection)
Python 将对象分为三代(0, 1, 2):
- 新创建对象 → 第 0 代。
- 经历一次 GC 未被回收 → 升到第 1 代。
- 再经历一次 → 升到第 2 代。
GC 频率:
- 第 0 代最频繁(默认每分配 700 个对象触发一次)
- 第 1 代较少
- 第 2 代最少
策略:大多数对象“朝生暮死”,优先回收年轻代,提高效率。
手动控制 GC:
| |
循环引用 + __del__ 方法的陷阱
如果循环引用中的对象定义了 __del__ 方法,Python 的 GC 无法确定销毁顺序,会将这些对象放入 gc.garbage 列表,不会自动回收!
| |
最佳实践:避免在可能形成循环引用的类中使用
__del__,改用weakref或上下文管理器。
弱引用(Weak Reference)——解决循环引用的利器
弱引用不增加对象的引用计数,因此不会阻止对象被回收。
| |
总结对比表:
| 概念 | 说明 | 优点 | 缺点/注意点 |
|---|---|---|---|
| 迭代器 | 实现 __iter__ 和 __next__ 的对象 | 标准协议,灵活可控 | 需手动管理状态 |
| 生成器 | 用 yield 定义的迭代器,自动保存状态 | 代码简洁,内存高效 | 一次性,不能回退 |
| 引用计数 | 对象被引用次数归零时立即回收 | 实时、高效 | 无法处理循环引用 |
| 循环引用 | 对象互相引用,导致引用计数 > 0 | 无(是问题) | 导致内存泄漏 |
| GC 机制 | 分代回收 + 标记清除,解决循环引用 | 自动处理循环引用 | 有性能开销;__del__ 可能导致回收失败 |
| 弱引用 | 不增加引用计数的引用 | 打破循环引用,避免内存泄漏 | 需手动检查是否失效 |
实际建议:
- 优先使用生成器处理大数据流,节省内存。
- 避免手动管理内存,Python 自动管理很可靠。
- 警惕循环引用,尤其在树形、图结构或父子关系中。
- 使用
weakref打破循环引用。 - 避免在复杂对象中使用
__del__,改用contextlib或显式清理方法。 - 监控内存:使用
tracemalloc、objgraph、gc.get_objects()等工具调试内存问题。
掌握这些概念,可以帮助写出更高效、更健壮、内存安全的 Python 代码!