关于python:嵌套函数的递归调用中的UnboundLocalError

UnboundLocalError in recursive call of nested function

本问题已经有最佳答案,请猛点这里访问。

我有以下python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def find_words(letters):
    results = set()

    def extend_prefix(w, letters):
        if w in WORDS: results.add(w)
        if w not in PREFIXES: return
        for L in letters:
            result = extend_prefix(w + L, letters.replace(L,"", 1))
            results = results | result

    extend_prefix('', letters)
    return results

print find_words("ABCDEFGH")

当我运行它时,我得到以下错误:

1
2
3
4
5
6
7
8
9
10
11
12
Traceback (most recent call last):
  File"ExtendPrefix.py", line 44, in <module>
    print find_words("ABCDEFGH")
  File"ExtendPrefix.py", line 41, in find_words
    extend_prefix('', letters)
  File"ExtendPrefix.py", line 38, in extend_prefix
    result = extend_prefix(w + L, letters.replace(L,"", 1))
  File"ExtendPrefix.py", line 38, in extend_prefix
    result = extend_prefix(w + L, letters.replace(L,"", 1))
  File"ExtendPrefix.py", line 35, in extend_prefix
    if w in WORDS: results.add(w)
UnboundLocalError: local variable 'results' referenced before assignment

显然,它在递归调用中找不到扩展前缀的结果。这是为什么?我如何修复?


因为您要在嵌套函数内部分配结果,所以python假定您使用的是本地范围的变量,并在第35行抛出,即使它是更高范围内的有效名称。如果您只是读取变量而不是写入它,那么它通常会在更高的名称空间对象上工作。但一旦出现赋值操作符,就跳到本地名称空间。

从python范围/名称空间:

A special quirk of Python is that – if no global statement is in
effect – assignments to names always go into the innermost scope.
Assignments do not copy data — they just bind names to objects.

要解决这个问题,最简单的方法是将要使用的变量传递到函数头中:

1
2
3
4
5
def extend_prefix(w, letters, results):
        if w in WORDS: results.add(w)
        if w not in PREFIXES: return
        for L in letters:
            extend_prefix(w + L, letters.replace(L,"", 1), results)

同样,在编写函数的过程中,没有返回集合,因此results = results | result可能会爆炸,结果为非类型。


或者,如果对结果使用默认值none,然后将结果初始化为空集,则不需要嵌套函数。它简化了您的代码并解决了您的问题。见下面…

1
2
3
4
5
6
7
def find_words(letters, pre = '', results = None):
    if results is None: results = set()
    if pre in WORDS: results.add(pre)
    if pre in PREFIXES:
        for L in letters:
            find_words(letters.replace(L, '', 1), pre+L, results)
    return results

很高兴在StackOverflow上看到另一个Udacity学生!