收到“UnboundLocalError: 在赋值前引用了局部变量'e'”的错误,尽管变量已被初始化

6 投票
2 回答
15623 浏览
提问于 2025-04-17 17:06

[社区编辑以提供可复现的示例:]

def main():
    e = None
    print(locals())
    while not e:
        try:
            raise Exception
        except Exception as e:
            pass            

main()

产生

~/coding$ python3.3 quiz2.py
{'e': None}
Traceback (most recent call last):
  File "quiz2.py", line 11, in <module>
    main()
  File "quiz2.py", line 5, in main
    while not e:
UnboundLocalError: local variable 'e' referenced before assignment

[已编辑]以包含可复现的代码

我正在尝试运行一个循环,这个循环的条件是当变量 e==None 时继续执行。相关的代码如下:

    print("\nThe current score list contains the following people's scores: ")
    score_list = open("score_list.dat", "rb")
    score_name = []
    e = None
    while not e:
        try:
            score = pickle.load(score_list)
            name = pickle.load(score_list)
            score_name.append([score, name])
        except EOFError as e:
            pass            
    score_list_sorted=sorted(score_list)
    sort_list.close()
    for item in score_list_sorted:
        print("Score: ", item[0], "\t", item[1])

完整代码在这里: https://www.dropbox.com/s/llj5xwexzfsoppv/stats_quiz_feb24_2013.py

它需要的数据文件(为了让测验运行)在这个链接: https://www.dropbox.com/s/70pbcb80kss2k9e/stats_quiz.dat

main() 需要编辑,以使用正确的数据文件地址:

我收到的完整错误信息如下。这很奇怪,因为我在 while 循环之前就初始化了 e。希望有人能帮我解决这个问题。谢谢!

Traceback (most recent call last):
  File "<pyshell#217>", line 1, in <module>
    main()
  File "/Users/Dropbox/folder/stats_quiz_feb24_2013.py", line 83, in main
    while not e:
UnboundLocalError: local variable 'e' referenced before assignment

2 个回答

15

这个错误是因为新的 try...except... 结构,这是 Python 3 的一个新特性。
你可以查看 PEP-3110 来了解更多。

在 Python 3 中,下面这个代码块

try:
    try_body
except E as N:
    except_body
...

在 Python 2.5 中是这样理解的

try:
    try_body
except E, N:
    try:
        except_body
    finally:
        N = None
        del N
...

所以在 Python 3 中,这个函数

def main():
    e = None
    print(locals())
    while not e:
        try:
            raise Exception
        except Exception as e:
            pass

其实是等同于

def main():
    e = None
    print(locals())
    if not e:
        try:
            raise Exception
        except Exception as e:
            pass
        del e
    if not e:
        try:
            raise Exception
        except Exception as e:
            pass
        del e
    ...

e 被初始化了,但在第一个 try except 块执行后就被删除了。
因此,UnboundLocalError 是不可避免的。

5

嗯,我不知道具体是什么导致了这个问题,但你为什么不在出现异常时直接用 break 呢?这样你的循环就变成了:

while True:
    try:
        score = pickle.load(score_list)
        name = pickle.load(score_list)
        score_name.append([score, name])
    except EOFError as e:
        break

据我所知,这是一种常见的做法,可以实现“在没有异常的情况下继续循环”。

编辑:为什么会这样

看起来在 Python 3 中,一旦你退出了异常处理的范围,和这个异常相关的变量就会从命名空间中移除。我把代码改成了下面这样:

def main():
    e = None
    print(locals())
    while not e:
        try:
            raise Exception
        except Exception as e:
            pass
        print(locals())

main()

输出:

{'e': None}
{}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in main
UnboundLocalError: local variable 'e' referenced before assignment

在 Python 2 中并不是这样。由于赋值异常给变量的语法发生了变化,我并不感到惊讶它的语义也发生了变化。虽然我觉得这种行为是“令人惊讶的”(因为这不是你所期待的结果)。

无论如何,当出现异常时,正确退出循环的方法就是上面的代码。如果你想在异常处理的范围之外保留这个异常,我想你可以这样做:

def main():
   e = None
   print(locals())
   while not e:
      try:
         raise Exception
      except Exception as ex:
         e = ex
      print(locals())

main()

这会产生以下输出:

{'e': None}
{'e': Exception()}

但对于你的具体用例来说,你真的不应该这样做。

撰写回答