我不了解这种python __del__的行为 [英] I don't understand this python __del__ behaviour
问题描述
有人可以解释为什么以下代码的行为方式:
Can someone explain why the following code behaves the way it does:
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)
The output (note that the destructor for d2 is never called) is this (python 2.7)
delete d1
after d1
func called
after d2
func called
delete d3
after d3
有没有一种方法可以修复"代码,以便在不删除添加的方法的情况下调用析构函数?我的意思是,放置d2.func = None的最佳位置将在析构函数中!
Is there a way to "fix" the code so the destructor is called without deleting the method added? I mean, the best place to put the d2.func = None would be in the destructor!
谢谢
[edit]根据前几个答案,我想澄清一下,我并不是在问使用__del__
的优点(或不足之处).我试图创建最短的函数,以证明我认为是非直觉的行为.我假设已经创建了循环引用,但是我不确定为什么.如果可能的话,我想知道如何避免使用循环引用....
[edit] Based on the first few answers, I'd like to clarify that I'm not asking about the merits (or lack thereof) of using __del__
. I tried to create the shortest function that would demonstrate what I consider to be non-intuitive behavior. I'm assuming a circular reference has been created, but I'm not sure why. If possible, I'd like to know how to avoid the circular reference....
推荐答案
我提供了自己的答案,因为尽管我对避免使用__del__
的建议表示赞赏,但我的问题是如何使代码正常工作?提供了示例.
I'm providing my own answer because, while I appreciate the advice to avoid __del__
, my question was how to get it to work properly for the code sample provided.
短版:以下代码使用weakref
来避免循环引用.我以为在发布问题之前已经尝试过此操作,但是我想我一定做错了什么.
Short version: The following code uses weakref
to avoid the circular reference. I thought I'd tried this before posting the question, but I guess I must have done something wrong.
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__
是 Bad .
Longer version:
When I posted the question, I did search for similar questions. I know you can use with
instead, and that the prevailing sentiment is that __del__
is BAD.
使用with
是有意义的,但仅在某些情况下才可以.打开文件,读取文件并将其关闭是一个很好的示例,其中with
是一个很好的解决方案.您已经找到了需要对象的特定代码块,并且想要清理对象和代码块的末尾.
Using with
makes sense, but only in certain situations. Opening a file, reading it, and closing it is a good example where with
is a perfectly good solution. You've gone a specific block of code where the object is needed, and you want to clean up the object and the end of the block.
数据库连接似乎经常被用作使用with
效果不佳的示例,因为您通常需要离开创建连接的代码部分,并在更多事件驱动的情况下关闭连接(而不是顺序的)时间范围.
A database connection seems to be used often as an example that doesn't work well using with
, since you usually need to leave the section of code that creates the connection and have the connection closed in a more event-driven (rather than sequential) timeframe.
如果with
不是正确的解决方案,我会看到两种选择:
If with
is not the right solution, I see two alternatives:
- You make sure
__del__
works (see this blog for a better description of weakref usage) - You use the
atexit
module to run a callback when your program closes. See this topic for example.
虽然我尝试提供简化的代码,但我的真正问题更多是由事件驱动的,因此with
不是合适的解决方案(with
适用于简化的代码).我还想避免使用atexit
,因为我的程序可以长时间运行,并且希望能够尽快执行清理.
While I tried to provide simplified code, my real problem is more event-driven, so with
is not an appropriate solution (with
is fine for the simplified code). I also wanted to avoid atexit
, as my program can be long-running, and I want to be able to perform the cleanup as soon as possible.
因此,在这种特定情况下,我发现它是使用weakref
并防止可能导致__del__
无法正常工作的循环引用的最佳解决方案.
So, in this specific case, I find it to be the best solution to use weakref
and prevent circular references that would prevent __del__
from working.
这可能是规则的例外,但是在某些情况下,使用weakref
和__del__
是正确的实现,恕我直言.
This may be an exception to the rule, but there are use-cases where using weakref
and __del__
is the right implementation, IMHO.
这篇关于我不了解这种python __del__的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!