Как принудительно удалить объект 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__
подняли?
4 ответов
способ закрытия ресурсов-это контекстные менеджеры, он же 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
удаляет последнюю ссылку so __del__
вызывается мгновенно. ГК в этом не участвует.
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__
as self
. После исключения в __init__
, объект обычно не будет иметь имени (Т. е. f =
часть не запускается) , поэтому их счетчик ссылок равен 0. Это означает, что объект удаляется нормально и __del__
называется.
в общем, чтобы убедиться, что что-то происходит, несмотря ни на что, вы используете
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.new
когда вы foo = Foo()
на самом деле происходит две вещи, сначала создается новый объект,__new__
, тогда он инициализируется,__init__
. Таким образом, вы не можете позвонить del foo
до того, как оба эти шага имели место. Однако, если есть ошибка в __init__
, __del__
будет вызываться, потому что объект был на самом деле уже создано в __new__
.
Edit: исправлено при удалении, если счетчик ссылок уменьшается до нуля.
возможно, вы ищете контекст менеджер?
>>> 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>
- добавить обработчик выхода это закрывает все бары.
-
__del__()
вызывается, когда количество ссылок на объект достигает 0 во время работы виртуальной машины. Это может быть вызвано GC. - если
__init__()
вызывает исключение, тогда объект считается неполным и__del__()
не будет вызван.