Базовый класс удален перед подклассом во время обработки __del__ Python - PullRequest
3 голосов
/ 25 мая 2010

Context

Мне известно, что если я задам вопрос о деструкторах Python, стандартным аргументом будет использование вместо этого контекстов. Позвольте мне начать с объяснения, почему я этого не делаю.

Я пишу подкласс для logging.Handler. Когда экземпляр закрывается, он отправляет значение стража в Queue.Queue. Если этого не произойдет, второй поток будет работать вечно, ожидая завершения Queue.Queue.get ().

Я пишу это для других разработчиков, поэтому я не хочу, чтобы сбой вызова 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

Кажется, определение Base удалено до вызова x.__del__().

Документация Python по __del __ () предупреждает, что подкласс должен вызвать базовый класс, чтобы получить чистое удаление, но здесь это кажется невозможным.

Вы видите, где я сделал плохой шаг?

Ответы [ 2 ]

2 голосов
/ 25 мая 2010

Ваш код немного вводит в заблуждение, я попробовал его, и он потерпел неудачу, как вы описали. Но потом я написал что-то вроде этого:

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__ методы во время выключения интерпретатора (я думаю, что объекты класса собираются перед экземплярами?), Но до этого момента они работают так, как ожидалось.

Может быть, достаточно сохранить основной поток живым до тех пор, пока другие не умрут, и не создавать экземпляры подкласса обработчика в основном потоке будет достаточно?

1 голос
/ 25 мая 2010

Одно решение, которое работает, но не может быть лучшим:

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"

Где вы храните вторую ссылку на базовый класс

...