Будет вызываться __del__
, если объект __init__
не завершен (например, путем исключения исключения)?
Вызывается ли del на объект, который не завершил init?
Ответ 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 также иллюстрируют последнюю ссылочную проблему, но я думаю, что пример фреймворка/обратного вызова более поразительный.