Python __del__ 处理时基类在子类之前被删除

3 投票
2 回答
3344 浏览
提问于 2025-04-15 23:07

背景

我知道如果我问关于Python析构函数的问题,大家通常会建议我使用上下文管理器。让我先解释一下我为什么不这样做。

我正在写一个logging.Handler的子类。当一个实例被关闭时,它会向一个Queue.Queue发送一个信号值。如果没有这样做,第二个线程就会一直运行,等待Queue.Queue.get()完成。

我写这个是为了其他开发者考虑,所以我不想因为没有调用handler对象的close()而导致程序卡住。

因此,我在__del__()方法中添加了一个检查,以确保对象被正确关闭。

我知道循环引用可能会导致某些情况下失败,但对此我无能为力。

问题

这里有一些简单的示例代码:

explicit_delete = True

class Base:
    def __del__(self):
        print "Base class cleaning up."

class Sub(Base):
    def __del__(self):
        print "Sub-class cleaning up."
        Base.__del__(self)

x = Sub()

if explicit_delete:
    del x

print "End of thread"

当我运行这个代码时,得到的结果是我预期的:

Sub-class cleaning up.
Base class cleaning up.
End of thread

如果我在第一行把explicit_delete设置为False,我得到的结果是:

End of thread
Sub-class cleaning up.
Exception AttributeError: "'NoneType' object has no attribute '__del__'" in <bound method Sub.__del__ of <__main__.Sub instance at 0x00F0B698>> ignored

看起来在调用x.__del__()之前,Base的定义就被移除了。

Python文档中关于__del__()的部分警告说,子类需要调用基类的方法以确保干净地删除,但在这里这似乎是不可能的。

你能看出我哪里出错了吗?

2 个回答

1

这里有一个可行的解决办法,不过可能不是最好的:

explicit_delete = False

class Base(object):
    def __del__(self):
        print "Base class cleaning up."

class Sub(Base):
    base = Base
    def __init__(self):
        print 'stating'

    def __del__(self):
        print "Sub-class cleaning up."
        self.base.__del__(self)

x = Sub()
if explicit_delete:
    del x

print "End of thread"

你可以保留一个对基类的第二个引用。

2

你的代码有点误导,我试了一下,果然像你说的那样失败了。不过我写了类似这样的代码:

import threading

class Base( object ):
    def __del__(self):
        print "Base class cleaning up."

class Sub(Base):
    def __del__(self):
        print "Sub-class cleaning up."
        Base.__del__( self )

def f():
    x = Sub()
    print "End of thread"

t = threading.Thread( target = f )
t.start()
t.join()

然后输出是:

End of thread
Sub-class cleaning up.
Base class cleaning up.
End of main thread.

所以我想在解释器关闭的时候,你不能依赖 __del__ 方法(我觉得类对象是在实例之前被回收的?),但在那之前它们的表现是正常的。

也许让主线程保持活着,直到其他线程都结束,并且不要在主线程中创建你的 Handler 子类实例,这样就足够了?

撰写回答