Logo
Section 1 / 30

固执的 del 操作

理解 del 如何通过引用计数决定 __del__ 何时调用

导入

固执的 del 操作

理解 del 如何通过引用计数决定 __del__ 何时调用

删除操作为何会如此复杂?

1class SomeClass: 2 def __del__(self): 3 print("Deleted!")

1.

让我们在交互式解释器中删除 SomeClass() 对象。

1>>> x = SomeClass() 2>>> y = x 3>>> del x # 这里应该打印 "Deleted!" 4>>> del y 5Deleted!
Terminal

终于删掉了。你可能已经猜到,在我们第一次尝试删除 x 时,是什么阻止了 __del__ 被调用。

2.

让我们让这个例子更复杂一些。

1>>> x = SomeClass() 2>>> y = x 3>>> del x 4>>> y # 检查 y 是否仍然存在 5<__main__.SomeClass instance at 0x7f98a1a67fc8> 6>>> del y # 和之前一样,这应该打印 "Deleted!" 7>>> globals() # 哦,它没有打印。我们检查一下所有全局变量来确认 8Deleted! 9{'__spec__': None, '__builtins__': <module 'builtins' (built-in)>, '__doc__': None, '__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__package__': None, 'SomeClass': <class '__main__.SomeClass'>}
Terminal

解释

  • del x 并不会直接调用 x.__del__()。

  • 当执行 del x 时,Python 会从当前作用域中删除名称 x,并将该对象的引用计数减 1。只有当对象的引用计数变为零时,才会调用 __del__()。

  • 在第二次输出中,__del__() 没有被调用,是因为前一条语句(>>> y)在交互式解释器中创建了对该对象的另一个引用(具体来说是 _ 这个魔法变量,它指向上一个非 None 表达式的返回值),从而阻止了引用计数在执行 del y 时归零。


Course Image
  • 调用 globals()(或任何产生非 None 结果的操作)导致 _ 引用了新的结果,从而丢弃了原有的引用。这一次,引用计数真正降到了 0,我们终于看到了 “Deleted!” 的输出。

简介

作用域之外的变量