Как принудительно удалить объект python?
Мне интересно узнать подробности о __del__
в python, когда и почему его следует использовать и для чего его не следует использовать. Я усердно изучил, что на самом деле это не совсем то, что наивно ожидалось от деструктора, поскольку это не противоположность __new__
/__init__
.
class Foo(object):
def __init__(self):
self.bar = None
def open(self):
if self.bar != 'open':
print 'opening the bar'
self.bar = 'open'
def close(self):
if self.bar != 'closed':
print 'closing the bar'
self.bar = 'close'
def __del__(self):
self.close()
if __name__ == '__main__':
foo = Foo()
foo.open()
del foo
import gc
gc.collect()
Я видел в документации, что не гарантируется __del__()
методы вызываются для объектов, которые все еще существуют, когда переводчик завершает работу.
- как можно гарантировать, что для любых экземпляров
Foo
, существующих при выходе интерпретатора, панель закрывается?
- в фрагменте кода выше бар закрывается на
del foo
или на gc.collect()
... или нет? если вы хотите более тонкое управление этими деталями (например, панель должна быть закрыта, когда объект не указан), каков обычный способ реализовать это?
- При вызове
__del__
гарантируется, что __init__
уже вызван? как насчет того, поднял ли __init__
?
Ответы
Ответ 1
Способы закрытия ресурсов - это менеджеры контекста, а также оператор with
:
class Foo(object):
def __init__(self):
self.bar = None
def __enter__(self):
if self.bar != 'open':
print 'opening the bar'
self.bar = 'open'
return self # this is bound to the `as` part
def close(self):
if self.bar != 'closed':
print 'closing the bar'
self.bar = 'close'
def __exit__(self, *err):
self.close()
if __name__ == '__main__':
with Foo() as foo:
print foo, foo.bar
выход:
opening the bar
<__main__.Foo object at 0x17079d0> open
closing the bar
2) Объекты Python удаляются, когда их счетчик ссылок равен 0. В вашем примере del foo
удаляет последнюю ссылку, поэтому __del__
вызывается мгновенно. GC не участвует в этом.
class Foo(object):
def __del__(self):
print "deling", self
if __name__ == '__main__':
import gc
gc.disable() # no gc
f = Foo()
print "before"
del f # f gets deleted right away
print "after"
выход:
before
deling <__main__.Foo object at 0xc49690>
after
gc
не имеет ничего общего с удалением ваших и большинства других объектов. Он там, чтобы очистить, когда простой подсчет ссылок не работает, из-за самореференций или круговых ссылок:
class Foo(object):
def __init__(self, other=None):
# make a circular reference
self.link = other
if other is not None:
other.link = self
def __del__(self):
print "deling", self
if __name__ == '__main__':
import gc
gc.disable()
f = Foo(Foo())
print "before"
del f # nothing gets deleted here
print "after"
gc.collect()
print gc.garbage # The GC knows the two Foos are garbage, but won't delete
# them because they have a __del__ method
print "after gc"
# break up the cycle and delete the reference from gc.garbage
del gc.garbage[0].link, gc.garbage[:]
print "done"
выход:
before
after
[<__main__.Foo object at 0x22ed8d0>, <__main__.Foo object at 0x22ed950>]
after gc
deling <__main__.Foo object at 0x22ed950>
deling <__main__.Foo object at 0x22ed8d0>
done
3) Давайте посмотрим:
class Foo(object):
def __init__(self):
raise Exception
def __del__(self):
print "deling", self
if __name__ == '__main__':
f = Foo()
дает:
Traceback (most recent call last):
File "asd.py", line 10, in <module>
f = Foo()
File "asd.py", line 4, in __init__
raise Exception
Exception
deling <__main__.Foo object at 0xa3a910>
Объекты создаются с помощью __new__
, а затем передаются в __init__
как self
. После исключения в __init__
у объекта обычно не будет имени (т.е. Часть f =
не будет запущена), поэтому их количество ссылок равно 0. Это означает, что объект обычно удаляется и вызывается __del__
.
Ответ 2
В общем, чтобы убедиться, что что-то происходит независимо от того, что вы используете
from exceptions import NameError
try:
f = open(x)
except ErrorType as e:
pass # handle the error
finally:
try:
f.close()
except NameError: pass
finally
будут выполняться блоки, независимо от того, есть ли ошибка в блоке try
, и есть ли ошибка в обработке ошибок, которая имеет место за except
блоков. Если вы не обрабатываете возбужденное исключение, оно все равно будет поднято после того, как finally
блок будет удален.
Общий способ убедиться, что файл закрыт, - это использовать "менеджер контекста".
http://docs.python.org/reference/datamodel.html#context-managers
with open(x) as f:
# do stuff
Это автоматически закроет f
.
Для вашего вопроса № 2 bar
закрывается сразу же, когда счетчик ссылок достигает нуля, поэтому на del foo
если нет других ссылок.
Объекты НЕ создаются __init__
, они создаются __new__
.
http://docs.python.org/reference/datamodel.html#object. новый
Когда вы делаете foo = Foo()
, на самом деле происходят две вещи: сначала создается новый объект __new__
, затем он инициализируется __init__
. Таким образом, вы не можете вызвать del foo
прежде чем эти шаги будут выполнены. Однако, если в __init__
есть ошибка, __del__
все равно будет вызываться, потому что объект уже был создан в __new__
.
Изменение: Исправлено, когда удаление происходит, если счетчик ссылок уменьшается до нуля.
Ответ 3
Возможно, вы ищете менеджер контекста ?
>>> class Foo(object):
... def __init__(self):
... self.bar = None
... def __enter__(self):
... if self.bar != 'open':
... print 'opening the bar'
... self.bar = 'open'
... def __exit__(self, type_, value, traceback):
... if self.bar != 'closed':
... print 'closing the bar', type_, value, traceback
... self.bar = 'close'
...
>>>
>>> with Foo() as f:
... # oh no something crashes the program
... sys.exit(0)
...
opening the bar
closing the bar <type 'exceptions.SystemExit'> 0 <traceback object at 0xb7720cfc>
Ответ 4
- Добавьте обработчик выхода, который закрывает все строки.
-
__del__()
вызывается, когда количество ссылок на объект достигает 0, пока виртуальная машина все еще работает. Это может быть вызвано GC.
- Если
__init__()
вызывает исключение, предполагается, что объект является неполным и __del__()
не будет вызываться.