Как заставить удаление объекта Python? - PullRequest
60 голосов
/ 21 июля 2011

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

  1. как можно гарантировать, что для любых Foo экземпляров, существующих при выходе из интерпретатора, панель закрывается?
  2. в приведенном выше фрагменте кода панель закрывается del foo или gc.collect() ... или ни того, ни другого?если вам нужно более точное управление этими деталями (например, панель должна быть закрыта, когда объект не связан), каков обычный способ реализовать это?
  3. при вызове __del__ гарантируется ли, что __init__ имеетуже звонили?что делать, если __init__ поднял?

Ответы [ 4 ]

69 голосов
/ 21 июля 2011

Способ закрытия ресурсов - контекстные менеджеры, или оператор 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

output:

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"

output:

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__.

8 голосов
/ 21 июля 2011

В общем, чтобы убедиться, что что-то происходит, несмотря ни на что, вы используете

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__.

Редактировать: исправлено, когда удаление происходит, если счетчик ссылок уменьшается до нуля.

5 голосов
/ 21 июля 2011

Возможно, вы ищете менеджер контекста ?

>>> 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>
2 голосов
/ 21 июля 2011
  1. Добавление обработчика выхода , который закрывает все столбцы.
  2. __del__() вызывается, когда число ссылок на объект достигает 0, когда виртуальная машина все еще работает.Это может быть вызвано GC.
  3. Если __init__() вызывает исключение, то объект считается неполным и __del__() не будет вызван.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...