我不理解这个python __del__行为
有人能解释一下为什么下面的代码会这样运行吗:
import types
class Dummy():
def __init__(self, name):
self.name = name
def __del__(self):
print "delete",self.name
d1 = Dummy("d1")
del d1
d1 = None
print "after d1"
d2 = Dummy("d2")
def func(self):
print "func called"
d2.func = types.MethodType(func, d2)
d2.func()
del d2
d2 = None
print "after d2"
d3 = Dummy("d3")
def func(self):
print "func called"
d3.func = types.MethodType(func, d3)
d3.func()
d3.func = None
del d3
d3 = None
print "after d3"
输出结果是这样的(注意,d2的析构函数从来没有被调用),这是在python 2.7下的结果:
delete d1
after d1
func called
after d2
func called
delete d3
after d3
有没有办法“修复”这段代码,让析构函数被调用,但又不删除添加的方法?我的意思是,最好的地方放d2.func = None应该是在析构函数里面!
谢谢
[编辑] 根据前面几个回答,我想澄清一下,我并不是在询问使用__del__
的优缺点。我试图创建一个最简短的函数,来展示我认为不太直观的行为。我假设创建了一个循环引用,但我不太确定为什么。如果可以的话,我想知道怎么避免循环引用……
8 个回答
你可以用 with
这个操作符来代替 __del__
。
https://web.archive.org/web/20201111211102/http://effbot.org/zone/python-with-statement.htm
就像处理文件对象一样,你可以这样做:
with Dummy('d1') as d:
#stuff
# d's __exit__ method is guaranteed to have been called
我自己来回答这个问题,因为虽然我明白大家建议不要使用 __del__
,但我想知道的是如何让它在我提供的代码示例中正常工作。
简短版本:以下代码使用 weakref
来避免循环引用。我原以为在发问之前已经尝试过这个方法,但看来我可能做错了什么。
import types, weakref
class Dummy():
def __init__(self, name):
self.name = name
def __del__(self):
print "delete",self.name
d2 = Dummy("d2")
def func(self):
print "func called"
d2.func = types.MethodType(func, weakref.ref(d2)) #This works
#d2.func = func.__get__(weakref.ref(d2), Dummy) #This works too
d2.func()
del d2
d2 = None
print "after d2"
详细版本:当我发问的时候,我确实搜索过类似的问题。我知道可以用 with
来替代,而且大家普遍认为 __del__
是 不好的。
使用 with
是有道理的,但只在某些情况下适用。比如打开一个文件、读取它,然后关闭,这就是一个很好的例子,使用 with
完全没问题。在这个特定的代码块中,你需要这个对象,并且希望在代码块结束时清理掉它。
数据库连接常常被用作不适合使用 with
的例子,因为你通常需要在创建连接的代码块之外关闭连接,而这通常是基于事件驱动的(而不是顺序执行的)。
如果 with
不是合适的解决方案,我看到两个替代方案:
虽然我试图提供简化的代码,但我真正的问题更像是事件驱动的,所以 with
并不是合适的解决方案(对于简化的代码来说 with
是可以的)。我还想避免使用 atexit
,因为我的程序可能会长时间运行,我希望尽快进行清理。
所以,在这个特定情况下,我认为使用 weakref
来防止循环引用是最好的解决方案,这样可以让 __del__
正常工作。
这可能是个例外,但在某些情况下,使用 weakref
和 __del__
是正确的实现,个人认为。