pygtk:在封闭作用域中引用了未赋值的自由变量

6 投票
2 回答
2618 浏览
提问于 2025-04-15 13:26

我遇到了一个很奇怪的作用域错误,连我自己都看不明白。在一个更新函数里面,我有一个嵌套的辅助函数,用来帮忙处理一些事情:

    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 个回答

0

我刚注册,没足够的积分来评论……

  • 全局或者更高层级没有名为 'es' 的变量吗?
  • attach_row 不是一个嵌套函数吗?
  • NameError 异常指向 ene 或 lne 函数中的 for 循环那一行吗?

一个可能的解决办法,但有点麻烦,可能是把 ene 和 lne 做成类,然后通过一个 __call__() 方法让它们像函数一样被调用。

4

这确实挺神秘的——看起来闭包在内部函数中消失了。我在想这是不是和 pygtk 如何处理这些回调函数有关(我对它的内部机制不太了解)。为了探究这个问题——如果你在 attach_row 的最后把 enelne 也添加到一个全局列表中,确保它们在某个地方“正常”保存,这样它们的闭包就能存活——这样做后问题还会存在吗?

如果问题依然存在,那我得承认这个问题实在是太神秘了,我同意之前的回答,建议使用一些更清晰的方式来保存状态的可调用对象(我建议用一个类实例的两个绑定方法,因为它们共享状态,但用两个单独的类实例,配合 __call__ 方法,并在它的 __init__ 中接收要设置的状态和事件框列表,这也是合理的——我认为有两个独立的类有点夸张;-)。

撰写回答