5.4. 实例化类

Python 中实例化类非常简单。要实例化一个类,只需像调用函数一样调用该类,并传递 __init__ 方法定义的参数。返回值将是新创建的对象。

示例 5.7. 创建 FileInfo 实例

>>> import fileinfo
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") 1
>>> f.__class__                                        2
<class fileinfo.FileInfo at 010EC204>
>>> f.__doc__                                          3
'store file metadata'
>>> f                                                  4
{'name': '/music/_singles/kairo.mp3'}
1 您正在创建一个 FileInfo 类(在 fileinfo 模块中定义)的实例,并将新创建的实例分配给变量 f。您正在传递一个参数,/music/_singles/kairo.mp3,它最终将作为 FileInfo__init__ 方法中的 filename 参数。
2 每个类实例都有一个内置属性,__class__,它是对象的类。(请注意,此处的表示包括实例在我的机器上的物理地址;您的表示将有所不同。)Java 程序员可能熟悉 Class 类,它包含 getNamegetSuperclass 等方法来获取有关对象的元数据信息。在 Python 中,可以通过 __class____name____bases__ 等属性直接在对象本身上获取此类元数据。
3 您可以像使用函数或模块一样访问实例的 docstring。类的所有实例共享相同的 docstring
4 还记得 __init__ 方法 将其 filename 参数分配给 self["name"] 吗?嗯,结果就在这里。创建类实例时传递的参数会直接发送到 __init__ 方法(以及对象引用 self,这是 Python 免费添加的)。
Note
Python 中,只需像调用函数一样调用类即可创建该类的新实例。没有像 C++Java 那样的显式 new 运算符。

5.4.1. 垃圾回收

如果创建新实例很容易,那么销毁它们就更容易了。通常,不需要显式释放实例,因为当分配给它们的变量超出范围时,它们会自动释放。内存泄漏在 Python 中很少见。

示例 5.8. 尝试实现内存泄漏

>>> def leakmem():
...     f = fileinfo.FileInfo('/music/_singles/kairo.mp3') 1
...     
>>> for i in range(100):
...     leakmem()                                          2
1 每次调用 leakmem 函数时,您都会创建一个 FileInfo 的实例,并将其分配给变量 f,它是函数内的局部变量。然后函数结束而没有释放 f,因此您可能会预期出现内存泄漏,但您错了。当函数结束时,局部变量 f 超出范围。此时,不再有任何对新创建的 FileInfo 实例的引用(因为您从未将其分配给 f 以外的任何内容),因此 Python 会为我们销毁该实例。
2 无论您调用 leakmem 函数多少次,它都不会泄漏内存,因为每次 Python 都会在从 leakmem 返回之前销毁新创建的 FileInfo 类。

这种形式的垃圾回收的技术术语是“引用计数”。Python 会保留对创建的每个实例的引用列表。在上面的示例中,只有一个对 FileInfo 实例的引用:局部变量 f。当函数结束时,变量 f 超出范围,因此引用计数降至 0Python 会自动销毁该实例。

在以前版本的 Python 中,存在引用计数失败的情况,并且 Python 无法为您清理。如果您创建了两个相互引用的实例(例如,双向链表,其中每个节点都有一个指向列表中上一个和下一个节点的指针),则这两个实例都不会被自动销毁,因为 Python(正确地)认为始终存在对每个实例的引用。Python 2.0 有一种称为“标记清除”的附加垃圾回收形式,它足够智能,可以注意到这种虚拟僵局并正确清理循环引用。

作为一名前哲学专业学生,我认为当没有人看着它们时,事物就会消失,这让我感到不安,但这正是 Python 中发生的事情。通常,您可以简单地忘记内存管理,让 Python 为您清理。

垃圾回收的进一步阅读