所以我正在开发一个Tkinter应用程序,它的结构有点复杂,而且子帧和父帧或不同对象之间经常有一些循环引用。你知道吗
Python2.7和3.4之前的版本不收集作为引用循环一部分的对象,当其中一个具有__del__
方法时,在Python3.4之后,解释器会更努力地尝试,但仍有一些情况下它不起作用(请参见this example)
有时会使用Tkinter变量(仅限StringVar和IntVar)。
这些类有一个__del__
方法,因此当它们是引用循环的一部分时,垃圾收集器不会收集循环中的任何对象。你知道吗
下面是一个使用pyobjgraph的最小可复制示例,以显示内存中存在的对象(您需要安装Tkinter、pyobjgraph和dot来运行此程序)。你知道吗
try :
import Tkinter as tk
except :
import tkinter as tk
class ParentWindow(tk.Frame):
def __init__(self, root):
self.intvarframes = []
self.root = root
self.spawn = tk.Button(root, text="spawn", command=lambda :self.intvarframes.append(FrameWithIntVar(self)))
self.remove = tk.Button(root, text="remove", command=lambda :self.intvarframes.pop().remove())
self.spawn.pack()
self.remove.pack()
def tryme(self, child):
print "child"+str(child)
class FrameWithIntVar:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(self.parent.root)
self.entry = tk.IntVar(self.frame)
self.entry.trace("w", lambda e : self.parent.tryme(self))
self.frame.pack()
self.bigobj = MyVeryBigObject()
c = tk.Checkbutton(self.frame, text="cb", variable=self.entry)
c.pack()
def remove(self):
self.frame.destroy()
#del self.entry
class MyVeryBigObject:
def __init__(self):
self.values = list(range(10**4))
root = tk.Tk()
ParentWindow(root)
root.mainloop()
import objgraph
if objgraph.by_type("MyVeryBigObject"):
objgraph.show_backrefs(objgraph.by_type("MyVeryBigObject"), max_depth=10, filename="test.dot")
from subprocess import check_call
check_call(['dot', '-Tpng', 'test.dot', '-o', 'test.png'])
else :
print ("No MyVeryBigObject in memory")
为了演示,只需启动应用程序,生成一些复选框,销毁它们并关闭应用程序,然后打开测试.png形象。
如您所见,MyVeryBigObject
的实例与您创建的复选框一样多。你知道吗
这里发生循环引用是因为lambda self.parent.tryme(self)
捕获self
(两次)。你知道吗
当我取消对remove
方法中的del self.entry
的注释时。对象被正确释放。你知道吗
请注意,这是一个简单的示例,在实际应用程序中,我必须手动将对帧的销毁传播到其所有子帧,以确保销毁所有变量。虽然它可以工作,但它意味着更多的代码,更多的维护,以及可能的常见错误,这些都是手动内存管理带来的。你知道吗
所以问题是:有没有更简单的方法?你知道吗
也许有一种方法可以让tkinter在一个帧被破坏时注意到,或者有一种方法可以在没有__del__
方法的情况下使用tkinter变量,但是我还没有找到任何东西。你知道吗
先谢谢你
Python 2或Python 3<;3.4
Tkinter中有一个
Destroy
事件,当子帧被销毁时会调用它,所以我只需将它添加到包含Tkinter变量的每个帧中使用
remove
方法删除当前对象中的条目和所有引用它的对象。你知道吗Python>;=3.4
所以在PEP 442之后,解释器似乎不再有处理这种特殊情况的困难,但我没有尝试过,所以如果有人能确认在Python>;3.4中运行我的示例时没有对象泄漏,那就太好了。你知道吗
请注意,虽然解释器更努力地释放对象,但仍有一些情况无法工作(请参见this example)
相关问题 更多 >
编程相关推荐