python 中的 __del__

933 阅读2分钟

最近注意到 Python 中有 __del__ 这个魔法函数,当一个对象被删除时会调用此函数。 那么这个删除具体是个什么概念呢?随即动手进行了些测试。

结论:当一个对象被 GC 回收时会调用其 __del__ 函数。

实验

背景

class Student:

    def __init__(self, name):
        self.name = name
    
    def __del__(self):
        print(f'deleting {self.name}')

case 1

In [4]: def test():
   ...:     amy = Student('Amy')
   ...:     del amy
   ...:     print('exit')
   ...:

In [5]: test()
deleting Amy
exit

手动 del 后,自动触发了 __del__

case 2

In [2]: def test():
   ...:     amy = Student('Amy')
   ...:     print('exit')
   ...:

In [3]: test()
exit
deleting Amy

即使没有调用 del,也触发了 __del__, 而且是在函数退出之后。 怀疑与 GC 有关,函数退出后,对象自动被 GC 回收,调用了 __del__

case 3

In [6]: def test():
   ...:     amy = Student('Amy')
   ...:     students = [amy]
   ...:     del amy
   ...:     print('exit')
   ...:

In [7]: test()
exit
deleting Amy

手动调用 del 后,没有随即触发 __del__,而是在函数退出后调用。看来 __del__del 没有直接关系,而是和 GC 关系更大。

del 删除的是引用这个对象的字面量,或者说是将这个字面量和对象解绑。 实际对象由于被容器对象引用,所以并没有随即销毁,也就没有立刻触发 __del__

case 4

In [8]: def test():
   ...:     amy = Student('Amy')
   ...:     amy = 1
   ...:     print('exit')
   ...:

In [9]: test()
deleting Amy
exit

amy 字面量另指向其他对象,原对象不再被引用,于是被 GC 回收,触发 __del__

结论

根据测试结果,有两种场景会触发其 __del__ 函数:

  • 手动调用 del 后,且对象不再被其他对象引用
  • 对象不再被引用,被 GC 回收

准确来说后者包含前者,即 当一个对象被 GC 回收时会调用其 __del__ 函数__del__ 与 del 没有直接关系。

彩蛋

那么如果把 GC 关掉,是不是就不会触发 __del__ 了呢?

In [2]: import gc; gc.disable()

In [3]: def test():
   ...:     amy = Student('Amy')
   ...:     print('exit')
   ...:

In [4]: test()
exit
deleting Amy

还是触发了 __del__,这是为什么呢?

不管关不关GC,程序退出的时候都得释放内存,所以始终都会触发 __del__ ,这是可以理解的。

gc.disable() 实际做了哪些事呢,看来还得好好看看 GC 呢。