Python中的__del__()方法有什么用?

What's the use of the __del__() method in Python?

来自Python documentation

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

据我所知,也没有办法保证对象在解释器退出之前停止存在,因为由垃圾收集器决定是否以及何时删除对象。

那么拥有这种方法有什么意义呢?您可以在其中编写清理代码,但不能保证它会被执行。

我知道你可以使用 try-finallywith 子句来解决这个问题,但我仍然想知道 __del__() 方法的一个有意义的用例是什么.

可用于处置对象管理的资源:https://github.com/python/cpython/blob/master/Lib/zipfile.py#L1805

如文档字符串中所述,这是一种最后的手段,因为只有当 gc 为 运行 时才关闭对象。

正如您在问题中所说,首选方法是自己调用 close,直接调用 .close() 或使用上下文管理器 with Zipfile() as z:

它基本上是用来强制调用一个方法,该方法应该在该对象所有 activity 完成后调用,喜欢

def __del__(self):
    self.my_func()

现在您确定 my_func 将调用对象工作完成的所有内容。

运行 这个程序,你就知道是怎么回事了

class Employee: 
  
    def __init__(self, name): 
        self.name = name
        print('Employee created.') 
    
    def get_name(self):
        return self.name

    def close(self):
        print("Object closed")

    # destructor
    def __del__(self):
        self.close()
  
obj = Employee('John')

print(obj.get_name())

# lets try deleting the object!
obj.__del__() # you don't need to run this

print("Program ends")

print(obj.get_name())

输出

> Employee created.
> John 
> Object closed 
> Program ends  
> John 
> Object closed

你说:

It is not guaranteed that del() methods are called for objects that still exist when the interpreter exits.

这是非常正确的,但在许多情况下,创建对象然后对这些对象的引用要么通过设置为 None 显式“销毁”,要么超出范围。这些对象在创建时或在执行过程中分配资源。当用户使用完对象时,他应该调用 closecleanup 方法来释放这些资源。但是最好有一个析构函数方法,即 __del__ 方法,当没有更多对对象的引用时调用该方法可以检查是否已调用该清理方法,如果没有调用清理本身.在 __del__ 可能不会在退出时被调用的情况下,此时回收资源可能不太重要,因为程序无论如何都会被终止(当然,在 cleanupclose 做的不仅仅是回收资源,还执行必要的终止功能,例如关闭文件,然后依赖 __del__ 在退出时调用确实会成为问题)。

重点是,在 reference-counting 实现中,例如 CPython,您可以依赖 __del__ 在对象的最后一个引用被销毁时被调用:

import sys

class A:
    def __init__(self, x):
        self.x = x

    def __del__(self):
        print(f'A x={self.x} being destructed.')


a1 = A(1)
a2 = A(2)
a1 = None
# a1 is now destroyed
input('A(1) should have been destroyed by now ...')
a_list = [a2]
a_list.append(A(3))
a_list = None # A(3) should now be destroyed
input('A(3) should have been destroyed by now ...')
a4 = A(4)
sys.exit(0) # a2 and a4 may or may not be garbage collected

打印:

A x=1 being destructed.
A(1) should have been destroyed by now ...
A x=3 being destructed.
A(3) should have been destroyed by now ...
A x=2 being destructed.
A x=4 being destructed.

除了对象 a2a4 之外,class A 的所有其他实例都将被“销毁”,即销毁。

实际用法例如,调用函数bar创建一个B的实例,该实例创建一个实例A。当函数 bar returns 对 B 的引用以及对 A 的引用被隐式销毁时,如果 close 方法在 A 实例尚未被调用:

class A:
    def __init__(self, x):
        self.x = x
        self.cleanup_done = False
 
    def close(self):
        print(f'A x={self.x} being cleaned up.')
        self.cleanup_done = True
 
 
    def __del__(self):
        if not self.cleanup_done:
            self.close()
 
 
class B:
    def __init__(self, x):
        self.a = A(x)
 
    def foo(self):
        print("I am doing some work")
 
 
def bar():
    b = B(9)
    b.foo()
 
def other_function():
    pass
 
if __name__ == '__main__':
    bar()
    other_function()

Python Demo

要使 B 实例显式调用 A 实例的 close 方法,它必须实现自己的 close 方法,然后将其委托给 A 实例的 close 方法。但是,为此目的使用 __del__ 方法毫无意义。如果那行得通,那么 A 实例自己的 __del__ 方法就足以进行清理。

__del()__ 像析构函数一样工作,有时它会通过垃圾收集器自动调用(我说有时并不总是因为你永远不知道它是否 运行 以及什么时候),所以它是用起来有点没用。

当对象被销毁时调用析构函数。在 Python 中,不像 C++ 中那样需要析构函数,因为 Python 有一个自动处理内存管理的垃圾收集器。

__del__() 方法在 Python 中被称为析构函数方法。当删除了对该对象的所有引用时调用它,即当一个对象被垃圾收集时。

析构函数声明的语法 :

def __del__(self):
  # body of destructor

注意 : 当对象失去引用或程序结束时,对对象的引用也会被删除。

示例1 : 这是析构函数的简单示例。通过使用 del 关键字,我们删除了对象“obj”的所有引用,因此自动调用了析构函数。

    # Python program to illustrate destructor 
    class Employee: 
      
        # Initializing 
        def __init__(self): 
            print('Employee created.') 
      
        # Deleting (Calling destructor) 
        def __del__(self): 
            print('Destructor called, Employee deleted.') 
      
    obj = Employee() 
    del obj 

#Output
#Employee created.
#Destructor called, Employee deleted.

注意 : 析构函数是在程序结束后或对象的所有引用被删除时调用的,即引用计数变为零时,不是对象超出范围。

Example 2 :这个例子给出了上述注意事项的解释。这里,注意析构函数是在“程序结束...”打印之后调用的。

# Python program to illustrate destructor 
  
class Employee: 
  
    # Initializing  
    def __init__(self): 
        print('Employee created') 
  
    # Calling destructor 
    def __del__(self): 
        print("Destructor called") 
  
def Create_obj(): 
    print('Making Object...') 
    obj = Employee() 
    print('function end...') 
    return obj 
  
print('Calling Create_obj() function...') 
obj = Create_obj() 
print('Program End...') 

#Output:
#Calling Create_obj() function...
#Making Object...

示例 3:现在,考虑以下示例:

# Python program to illustrate destructor 
  
class A: 
    def __init__(self, bb): 
        self.b = bb 
  
class B: 
    def __init__(self): 
        self.a = A(self) 
    def __del__(self): 
        print("die") 
  
def fun(): 
    b = B() 
  
fun() 

#Output:
#die

在此示例中,当函数 fun() 被调用时,它创建了一个 class B 的实例,该实例将自身传递给 class A,然后后者设置对 class 的引用B 并导致循环引用。

通常,Python 用于检测这些类型的循环引用的垃圾收集器会将其删除,但在本例中,自定义析构函数的使用将此项标记为“不可收集”。 简单地说,它不知道销毁对象的顺序,所以它离开了它们。因此,如果您的实例涉及循环引用,它们将在内存中存在的时间与应用程序 运行.

一样长。

来源:Destructors in Python

在阅读了所有这些答案后——none 其中令人满意地回答了我所有的 questions/doubts——并重新阅读了 Python 文档,我得出了自己的结论。这是我对此事的看法总结。


Implementation-agnostic

您从 __del__ method documentation 中引用的段落说:

It is not guaranteed that the __del__() methods are called for objects that still exist when the interpreter exits.

但不仅 保证在解释器退出期间调用 __del__() 对象被销毁,甚至不能保证对象被垃圾收集 完全,即使在正常执行期间—from the "Data model" section of the Python Language Reference:

Objects are never explicitly destroyed; however, when they become unreachable they may be garbage-collected. An implementation is allowed to postpone garbage collection or omit it altogether — it is a matter of implementation quality how garbage collection is implemented, as long as no objects are collected that are still reachable.

因此,回复您的问题:

So what's the point of having this method at all? You can write cleanup code inside it, but there's no guarantee it will ever be executed.

从 implementation-agnostic 的角度来看,__del__ 方法作为可以依赖的代码的基本组成部分是否有任何用途? 没有。 None完全没有。从这个角度看本质上是没有用的。

不过,从实际的角度来看,正如其他答案所指出的,您可以使用 __del__ 作为 last-resort 机制来(尝试)确保在之前执行任何必要的清理对象被销毁,例如释放资源,如果用户忘记显式调用 close 方法。这与其说是 fail-safe,不如说是“添加额外的安全机制并没有坏处,即使它不能保证工作”——事实上,most Python 实现将捕获 大多数 的时间。但这没什么依赖


Implementation-specific

话虽这么说,如果您知道您的程序将 运行 在一组特定的 Python 实现上,那么您可以依赖垃圾收集的实现细节——例如,如果您使用 CPython,你可以“依赖”这样一个事实,即在正常执行期间(即在解释器退出之外),如果 non-cyclically-referenced 对象的引用计数达到零,它将被垃圾收集并且它的 __del__ 方法将被调用,正如其他答案所指出的那样。来自与上述相同的小节:

CPython implementation detail: CPython currently uses a reference-counting scheme with (optional) delayed detection of cyclically linked garbage, which collects most objects as soon as they become unreachable, but is not guaranteed to collect garbage containing circular references.

但是,这仍然是非常不稳定的,并且不能真正依赖它,因为如前所述,它只对不属于循环引用图的对象有保证。 还有:

Other implementations act differently and CPython may change. Do not depend on immediate finalization of objects when they become unreachable (so you should always close files explicitly).


底线

从纯粹的角度来看,__del__方法是完全没有用的。从稍微不那么纯粹的角度来看,它仍然几乎没有用。从实用的角度来看,它可能作为代码的 补充 有用——但绝不是 必不可少的 特性。