如何强制删除一个Python对象?
我对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__()
方法。
- 如何确保在解释器退出时,任何存在的
Foo
实例的“酒吧”都会被关闭? - 在上面的代码片段中,
del foo
时“酒吧”会被关闭,还是在gc.collect()
时关闭,或者都不是?如果你想更精细地控制这些细节(例如,当对象不再被引用时“酒吧”应该关闭),通常该怎么实现? - 当
__del__
被调用时,是否可以保证__init__
已经被调用过?如果__init__
抛出异常呢?
4 个回答
也许你在找一个上下文管理器?
>>> 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>
一般来说,如果你想确保某件事情无论如何都会发生,你可以使用
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
。
关于你的第二个问题,bar
会在引用计数变为零时立即关闭,所以在 del foo
的时候,如果没有其他引用的话。
对象并不是通过 __init__
创建的,而是通过 __new__
创建的。
http://docs.python.org/reference/datamodel.html#object.new
当你执行 foo = Foo()
时,实际上发生了两件事,首先是创建了一个新对象,__new__
,然后对它进行初始化,__init__
。所以在这两个步骤完成之前,你不可能调用 del foo
。不过,如果在 __init__
中出现错误,__del__
仍然会被调用,因为对象实际上已经在 __new__
中创建了。
编辑:修正了当引用计数降到零时删除发生的情况。
关闭资源的方式是使用上下文管理器,也就是 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__
创建的,然后作为 self
传递给 __init__
。如果在 __init__
中发生异常,通常这个对象不会有名字(也就是说 f =
这一部分没有执行),所以它的引用计数是0。这意味着这个对象会被正常删除,并且 __del__
会被调用。