How to force deletion of a python object?

前端 未结 4 1047
逝去的感伤
逝去的感伤 2020-11-30 19:36

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 n

相关标签:
4条回答
  • 2020-11-30 19:52

    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.

    0 讨论(0)
    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.
    0 讨论(0)
  • 2020-11-30 20:03

    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.

    0 讨论(0)
  • 2020-11-30 20:10

    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>
    
    0 讨论(0)
提交回复
热议问题