pygtk:在封闭作用域中引用自由变量之前未分配

6

非常奇怪的作用域错误,我甚至看不到。在更新程序函数内部,我有一个嵌套的辅助函数来......帮助处理某些事情:

    def attach_row(ws,r1,r2):
        es = []
        for i,w in enumerate(ws):
            eb = gtk.EventBox()
            a = gtk.Alignment(xalign=0.0,yalign=0.5)
            a.add(w)
            eb.add(a)
            eb.set_style(self.rowStyle.copy())
            es.append(eb)                
            self.table.attach(eb, i, i+1, r1, r2,
                              xoptions=gtk.EXPAND|gtk.FILL,
                              yoptions=gtk.SHRINK)

        def ene(_,ev):
            for eb in es:
                eb.set_state(gtk.STATE_PRELIGHT)
        def lne(_,ev):
            for eb in es:
                eb.set_state(gtk.STATE_NORMAL)
        for eb in es:                
            eb.connect('enter-notify-event', ene)
            eb.connect('leave-notify-event', lne)

这个有时候能够正常工作,但如果update()函数运行得太频繁,最终会出现以下错误:

    for eb in es:
NameError: free variable 'es' referenced before assignment in enclosing scope

是什么导致了这个问题?es肯定在那些函数被调用之前就被分配了。对吗?是否发生了一些奇怪的事情,即在创建新行时调用了先前创建的行的ene(),并且闭合的es被覆盖了?

2个回答

4
相当神秘——看起来闭包从内部函数中消失了。不知道这是否与pygtk如何保存这样的回调函数有关(我不熟悉其内部机制)。为了探究这一点,如果您在attach_row结尾处还将ene和lne附加到全局列表中,只是为了确保它们被“正常”地保存在某个地方,使其闭包能够存活,那会发生什么呢?
如果是这种情况,那么我必须承认这个问题实在是太神秘了,并同意之前的答案建议使用可调用对象以更清晰的方式保存其状态(我建议使用一个类实例的两个绑定方法,因为它们共享状态,但一个类的两个实例也可以通过__call__接收要设置的状态和事件框列表的方式来实现,这也是合理的——使用两个单独的类在我看来有点夸张;-)。

呵呵,没错。我其实意识到了一种更合理的方法——不是删除和添加很多这些行,而是创建一个集合并在其中更改小部件。 不过需要注意的是:我尝试将“es”作为用户数据传递给那些ene和lne函数。结果是我不再收到NameError错误,但小部件根本不会高亮显示。似乎还是有些东西丢失了。 如果这种情况再次出现,我会尝试使用类的想法。 - Claudiu
更好的方法是使用VBox,每行放置一个事件框,然后使用sizegroup来对齐列。 - Claudiu
是的,在其他情况下也发生过这种情况。我认为这是 Python 2.5 的怪异之处。至今未找到答案。 - Claudiu
我们在PyQt中看到了类似的情况,当我们过早地删除外部对象时。因此,我相信这个答案是正确的,即闭包消失,因为底层的本地对象被销毁了(因此Python包装器也被销毁),但是本地函数仍然存在,但只有Python知道,而不是GTK。 - Macke

0

还没有足够的积分来留下评论(刚注册)...

  • 全局变量或更高作用域中没有 'es' 变量吗?
  • attach_row 也不是一个嵌套函数吗?
  • NameError 异常指向 ene 或 lne 函数中的 for 循环行吗?

一种可能的解决办法,但有点麻烦的方法是创建 ene 和 lne 类,通过 __call__() 方法实例化并可调用为函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接