Подтвердить что ты не робот

Вызывается ли del на объект, который не завершил init?

Будет вызываться __del__, если объект __init__ не завершен (например, путем исключения исключения)?

4b9b3361

Ответ 1

class test():
    def __init__(self):
        raise

    def __del__(self):
        print "__del__ called"

try:
    test()
except:
    pass

Да.

Объяснение: __del__ вызывается при удалении последней ссылки на объект. Но если вы не поймаете исключение, __del__ не будет вызываться, потому что python все еще сохраняет ссылку на объект в стеке, и он сохраняется, пока программа не выйдет, чтобы распечатать трассировку. Если вы поймаете и обработаете исключение, объект будет удален, как только вся информация, относящаяся к исключению, будет удалена из стека.

Конечно, __del__ не будет успешно запускаться, если программа вот-вот прекратит работу, если не будет предпринята осторожность, а в некоторых случаях даже нет - см. __del__.

Addenum: Cédrik Julien сказал в своем ответе (теперь исправленный): "Если __new__ вызвало исключение, ваш объект не будет создан, а __del__ не будет вызываться". Это не всегда правильно. Здесь пример, где __del__ вызывается, хотя исключение выражено в __new__:

class test():
    def __new__(cls):
        obj = object.__new__(cls)
        raise
        return obj

    def __del__(self):
        print "__del__ called"

Таким образом, какое-то исключение произошло, когда мы добрались до объекта test obj, прежде чем возвращать его. Но поскольку объект уже создан, вызывается __del__. Урок: в методе __del__ не принимайте ничего, что должно было произойти после того, как object.__new__() на самом деле произошло. В противном случае вы можете создать исключение, пытающееся получить доступ к несуществующему атрибуту или полагаться на какое-то другое допущение, которое недействительно. Исключение будет проигнорировано, но любая задача __del__ должна была завершиться неудачно.

Ответ 2

Ответ ДА.

Как обсуждалось здесь __del__ не является противоположностью __init__, а просто oppposite __new__.

Если __new__ вызывает исключение перед созданием объекта (вообще, перед вызовом суперкласса), ваш объект не будет создан, а __del__ не будет вызван, в противном случае объект будет создан, а __del__ будет называться.

Метод __init__ - это поздний инициализатор, конструктор реального объекта - это метод __new__().

Ответ 3

РЕДАКТИРОВАТЬ: Если на ваш вопрос вызывается "__del__()", тогда ответ будет следующим: Да.

Если, OTOH, ваш вопрос: "Будет ли это вызвано немедленно?", тогда ответ будет следующим:

Нет, не обязательно:

class Parent(object):
    def __init__(self):
        try:
            Child(self)
        except:
            pass

class Child(object):
    def __init__(self, parent):
        parent.child = self
        raise
    def __del__(self):
        print 'Child.__del__()'

p = Parent()
print 'hu?'
print p.child
print 'wha?'

Вывод:

hu?
<__main__.Child object at 0x7ff4674c>
wha?
Child.__del__()

Child.__del__() здесь не вызывается, когда Child.__init__() терпит неудачу, но когда p - сбор мусора, который заканчивается.

Ответ 4

Второй ответ лазиров. Тем не менее, я хотел бы подчеркнуть точку в последней ссылке.

Если вы передадите self вокруг другим объектам (например, при регистрации обратных вызовов) до того, как возникнет исключение, ваш объект все равно будет жив, т.е. __del__ не будет вызываться:

>>> keeprefs = []
>>> class X(object):
...     def __init__(self):
...         keeprefs.append(self)
...         raise Exception()
...     def foobar(self):
...         print("this could be a zombie foobar")
... 
>>> x = X()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __init__
Exception
>>> keeprefs
[<__main__.X object at 0x7f9e2630bc50>]
>>> keeprefs[0].foobar()
this could be a zombie foobar

В реальной жизни keeprefs может быть, например, структура, в которой вы регистрируете методы обратного вызова для некоторых событий. Поэтому, когда ваше исключение возбуждено, инфраструктура не распознает это и с радостью называет методы вашего, по-видимому, никогда не созданного объекта.

Ответы

@pillmuncher также иллюстрируют последнюю ссылочную проблему, но я думаю, что пример фреймворка/обратного вызова более поразительный.