缺少声明语句的情况——对象被“垃圾回收”了 | 第二部分 类型与操作 —— 第 6 章: 动态类型的插曲 |《学习 python:强大的面向对象编程(第 5 版)》| python 技术论坛-大发黄金版app下载
在上一节的代码列表中,在每个赋值语句中给变量a
分配了不同类型的对象。但在重新赋值变量时,它之前引用的那个值会发生什么呢?比如,在下面的语句之后,对象3会发生什么?
>>> a = 3
>>> a = 'spam'
答案是在python中,当名称被分配给对象时,之前对象所占用的空间如果没有被其他任何名称或对象所引用就会被回收。对象空间的这个自动回收被称为垃圾回收,它让支持它的语言如python的程序员的生活变得更简单。
考虑下面的例子来展示,它在每个赋值语句上将名称x
设置为不同对象:
>>> x = 42
>>> x = 'shrubbery' # 现在回收42 (除非在其他地方被引用)
>>> x = 3.1415 # 现在回收'shrubbery'
>>> x = [1, 2, 3] # 现在回收 3.1415
首先,注意每次x
被设定为一个不同的对象类型。再说一次,虽然并不真的如此,效果却是像x
的类型在随着时间而变化。记住,在python中类型与对象共存,而非名称。因为名称只是对象的通用引用,所以这样的代码工作得很自然。
第二,注意对象引用在这个过程中被丢弃。每次x
被分配一个新对象时,python都会回收之前对象的空间。比如,当它被分配了字符串'shrubbery'时,对象42立即就被回收了(假设它没有在其他任何地方被引用)——也就是说,对象空间被自动抛进了自由空间池,准备为未来的对象所用。
在内部,python通过在每个对象中维持一个计数器(跟踪当前指向那个对象的引用数)来达到这个成就。只要(并且刚好在)这个计数器减为0时,对象的内存空间就自动被回收。在之前的代码列表中,假设每次x
都被分配一个新对象,上一个对象的引用计数降为0,导致它被回收。
垃圾回收最立即可见的好处是它意味着可以自由使用对象而无需在脚本中分配或释放空间。python将在程序运行时为你清理未使用的空间。在实践中,这消除了大量的在底层语言如c和c 中必须的跟踪管理代码。
关于python垃圾收集的更多信息
技术上来说,python的垃圾收集主要基于这里描述的引用计数器;然而,它还有一个组件,可以及时探测和回收带有循环引用的对象。如果确认代码没有创建循环,这个组件可以被禁用,但它默认是开启的。
循环引用在引用计数垃圾收集器中是一个经典的问题。因为引用被实现为指针,所以一个对象可能引用自身,或引用另一个引用自身的对象。比如,在第一部分末尾的练习3和它在附录d中的解法显示了如何通过在列表中嵌入对它自身的引用来创建一个循环(比如:l.append(l))。同样的现象可以发生对由用户自定义类所创建的对象的属性的赋值中。虽然相对少见,但因为这些对象的引用数从不减少为0,所以它们必须被特殊处理。
要得到关于python的循环探测器的更多细节,请参考python库手册的gc
模块的文档。这里最好的消息是python中基于垃圾收集的内存管理是被高度精通此事的人来为你实现的。
还注意本章python垃圾回收器的描述只适用于标准python(又名 cpython);第2章的可选实现如jython,ironpython,和pypy可能使用不同的方案,然而所有方案的最终结果都是类似的——会自动为你回收未使用的空间,如果并不总是立即地回收。