66

我很好奇__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

4 回答 4

74

关闭资源的方法是上下文管理器,也就是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)当引用计数为 0 时,Python 的对象将被删除。在您的示例中,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__as self。在 中出现异常后__init__,该对象通常没有名称(即该f =部分未运行),因此它们的引用计数为 0。这意味着该对象被正常删除并被__del__调用。

于 2011-07-21T07:55:19.540 回答
8

通常,为了确保无论如何都会发生某些事情,您使用

from exceptions import NameError

try:
    f = open(x)
except ErrorType as e:
    pass # handle the error
finally:
    try:
        f.close()
    except NameError: pass

finallytry无论块中是否存在错误,以及块中发生的任何错误处理是否存在错误,块都将运行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__

编辑:如果引用计数减少到零,则在删除发生时更正。

于 2011-07-21T07:21:25.067 回答
5

也许您正在寻找上下文管理器

>>> 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>
于 2011-07-21T07:21:53.513 回答
2
  1. 添加一个关闭所有柱的退出处理程序。
  2. __del__()当 VM 仍在运行时对对象的引用数达到 0 时调用。这可能是由 GC 引起的。
  3. 如果__init__()引发异常,则假定该对象不完整并且__del__()不会被调用。
于 2011-07-21T07:17:38.690 回答