如何强制删除Python对象?

发布于 2024-11-25 03:41:06 字数 991 浏览 7 评论 0原文

我很好奇Python中__del__的细节,何时、为什么应该使用它以及它不应该用于什么。我经历了惨痛的教训才知道,它并不像人们天真地期望的析构函数那样,因为它不是 __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__ 引发怎么办?

I am curious about the details of __del__ in python, when and why it should be used and what it shouldn't be used for. I've learned the hard way that it is not really like what one would naively expected from a destructor, in that it is not the opposite of __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()

I saw in the documentation that it is not guaranteed __del__() methods are called for objects that still exist when the interpreter exits.

  1. how can it be guaranteed that for any Foo instances existing when the interpreter exits, the bar is closed?
  2. in the code snippet above does the bar get closed on del foo or on gc.collect()... or neither? if you want finer control of those details (e.g. the bar should be closed when the object is unreferenced) what is the usual way to implement that?
  3. when __del__ is called is it guaranteed that __init__ has already been called? what about if the __init__ raised?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

陌生 2024-12-02 03:41:06

关闭资源的方法是上下文管理器,又名 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__ 作为自我。在__init__中发生异常后,该对象通常不会有名称(即f =部分不运行),因此它们的引用计数为0。这意味着对象被正常删除并调用__del__

The way to close resources are context managers, aka the with statement:

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's objects get deleted when their reference count is 0. In your example the del foo removes the last reference so __del__ is called instantly. The GC has no part in this.

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

The gc has nothing to do with deleting your and most other objects. It's there to clean up when simple reference counting does not work, because of self-references or circular references:

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"

output:

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) Lets see:

class Foo(object):
    def __init__(self):

        raise Exception

    def __del__(self):
        print "deling", self

if __name__ == '__main__':
    f = Foo()

gives:

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>

Objects are created with __new__ then passed to __init__ as self. After a exception in __init__, the object will typically not have a name (ie the f = part isn't run) so their ref count is 0. This means that the object is deleted normally and __del__ is called.

凉城 2024-12-02 03:41:06

一般来说,为了确保无论如何都会发生某些事情,

from exceptions import NameError

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

无论 try 块中是否有错误,以及是否有错误,您使用的 finally 块都会运行。是 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.< strong>new

当你执行 foo = Foo() 时,实际上发生了两件事,首先创建了一个新对象,__new__,然后它正在初始化,__init__。因此,在这两个步骤发生之前,您不可能调用 del foo 。然而,如果__init__中出现错误,__del__仍然会被调用,因为该对象实际上已经在__new__中创建了。

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

In general, to make sure something happens no matter what, you use

from exceptions import NameError

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

finally blocks will be run whether or not there is an error in the try block, and whether or not there is an error in any error handling that takes place in except blocks. If you don't handle an exception that is raised, it will still be raised after the finally block is excecuted.

The general way to make sure a file is closed is to use a "context manager".

http://docs.python.org/reference/datamodel.html#context-managers

with open(x) as f:
    # do stuff

This will automatically close f.

For your question #2, bar gets closed on immediately when it's reference count reaches zero, so on del foo if there are no other references.

Objects are NOT created by __init__, they're created by __new__.

http://docs.python.org/reference/datamodel.html#object.new

When you do foo = Foo() two things are actually happening, first a new object is being created, __new__, then it is being initialized, __init__. So there is no way you could possibly call del foo before both those steps have taken place. However, if there is an error in __init__, __del__ will still be called because the object was actually already created in __new__.

Edit: Corrected when deletion happens if a reference count decreases to zero.

琉璃梦幻 2024-12-02 03:41:06

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

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

Perhaps you are looking for a context manager?

>>> 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>
苏璃陌 2024-12-02 03:41:06
  1. 添加一个关闭所有栏的 退出处理程序
  2. 当虚拟机仍在运行时对象的引用数量达到 0 时,__del__() 将被调用。这可能是GC引起的。
  3. 如果 __init__() 引发异常,则假定该对象不完整,并且 __del__() 将不会被调用。
  1. Add an exit handler that closes all the bars.
  2. __del__() gets called when the number of references to an object hits 0 while the VM is still running. This may be caused by the GC.
  3. If __init__() raises an exception then the object is assumed to be incomplete and __del__() won't be invoked.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文