Python:NameError:在封闭作用域中分配之前引用自由变量“re”

21

我在Python 3.3.1 (win7)中遇到了一个奇怪的NameError。

代码如下:

import re

# ...

# Parse exclude patterns.
excluded_regexps = set(re.compile(regexp) for regexp in options.exclude_pattern)

# This is line 561:
excluded_regexps |= set(re.compile(regexp, re.I) for regexp in options.exclude_pattern_ci)

错误:

Traceback (most recent call last):
  File "py3createtorrent.py", line 794, in <module>
    sys.exit(main(sys.argv))
  File "py3createtorrent.py", line 561, in main
    excluded_regexps |= set(re.compile(regexp, re.I) for regexp in options.exclude_pattern_ci)
  File "py3createtorrent.py", line 561, in <genexpr>
    excluded_regexps |= set(re.compile(regexp, re.I) for regexp in options.exclude_pattern_ci)
NameError: free variable 're' referenced before assignment in enclosing scope

请注意,发生错误的561行是上面代码中的第二行。换句话说:re不是一个自由变量。它只是正则表达式模块,可以在第一行中完全引用。

在我看来,对re.I的引用似乎是问题的原因,但我不明白具体原因。


如果您从第561行中删除re.I,错误是否会消失?我怀疑不会 - 问题很可能是由于您在问题中遗漏了某些内容所致。 - alexis
尝试提供一个 http://sscce.org/。 - glglgl
@alexis 你是对的。如果我删除 re.I,错误不会消失。 - robert
3个回答

23

很可能在同一函数内的第561行以下某个地方(可能是无意中)对re进行了赋值,这会导致你的错误:

import re

def main():
    term = re.compile("foo")
    re = 0

main()

2
你是对的。我在那行以下的某个地方再次导入了re模块(本地到函数作用域)。然而,我不明白为什么Python没有抱怨第一行... - robert
很高兴它奏效了。如果你能够构造一个最小化的示例来复制你的问题,我们可以为你解释Python语义。目前你提供的信息实在不够。 - alexis
这是一个重现错误的脚本:http://pastebin.com/yQN89y4A Python在第13行抛出NameError(而不是第12行)。 - robert
2
你的代码在第12行没有抛出异常,因为可迭代对象为空;所以生成器表达式体没有运行。 - SingleNegationElimination

12

在回溯中的“自由变量”表明这是封闭作用域中的局部变量。可能像这样:

 baz = 5

 def foo():
     def bar():
         return baz + 1

     if False:
          baz = 4

     return bar()

这样就可以使baz引用到一个局部变量(其值为4的变量),而不是(可能也存在的)全局变量。要修复它,将baz强制转换为全局变量:

 def foo():
     def bar():
         global baz
         return baz + 1

这样它就不会尝试解析名称为baz的非本地版本。最好的做法是找到您正在使用 re 的方式看起来像是局部变量 (生成器表达式/列表推导式是一个好方法) 并将其命名为其他名称。


1
其他的解释都很好,但是我想再加上一个我刚刚发现的变体。由于Python 3 更喜欢迭代器而不是“物理”列表,所以我们必须更加小心:
def foo():
    re = 3
    faulty = filter(lambda x: x%re, range(30))
    del re
    return faulty
list(foo())

过滤器表达式只在最后一行的返回语句之后才会被评估,特别是在del re之后。因此,最后一行会由于错误而中断:
NameError: free variable 're' referenced before assignment in enclosing scope

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