python和UnboundLocalError

时间:2012-03-15 13:25:38

标签: python

我对局部变量和python(2.7)有点问题。

我有一些代码:

def foo(a):
    def bar():
        print a
    return bar()

>>>foo(5)
5

嗯,它正在工作,但是如果想修改一个,就像这样:

def foo(a):
    def bar():
        a -= 1
    return bar()
>>>foo(5) 
UnboundLocalError: local variable 'a' referenced before assignment

所以我必须将'a'影响到另一个变量。

但我不明白这种情况。 是因为当有一个赋值时,python会查找locals()变量并找不到它吗?

感谢。

2 个答案:

答案 0 :(得分:8)

你在Python中发现了一些曾经存在问题的东西!简短的回答是,您不能在Python 2.x中执行此操作(尽管您可以simulate),但您可以使用nonlocal关键字在3.x中执行此操作。

请参阅PEP 3104

  

在2.1版之前,Python对范围的处理类似于   标准C:在一个文件中只有两个范围的级别,全局   和当地的。在C中,这是事实的自然结果   函数定义不能嵌套。但在Python中,虽然功能   通常在顶层定义,函数定义可以   执行任何地方这给了Python嵌套的句法外观   没有语义的范围,并产生了不一致   令一些程序员感到惊讶 - 例如,递归函数   在顶层工作时,在内部移动时将停止工作   另一个函数,因为递归函数自己的名字不会   在身体的范围内可以看得更久。这违反了直觉   放置在不同的函数时,函数应该表现一致   上下文。这是一个例子:

def enclosing_function():
    def factorial(n):
        if n < 2:
            return 1
        return n * factorial(n - 1)  # fails with NameError
    print factorial(5)
     

Python 2.1通过使其可见而更接近静态嵌套作用域   绑定在所有封闭范围内的名称(参见PEP 227)。这种变化使   上面的代码示例按预期工作。但是,因为任何   对名称的赋值隐式声明该名称是本地的,它是   不可能在外部范围内重新绑定名称(全局除外)   声明强制名称为全局名称。因此,以下代码,   旨在显示可以递增和递减的数字   通过单击按钮,不能像熟悉词汇的人那样工作   范围可能会有所期待:

def make_scoreboard(frame, score=0):
    label = Label(frame)
    label.pack()
    for i in [-10, -1, 1, 10]:
        def increment(step=i):
            score = score + step  # fails with UnboundLocalError
            label['text'] = score
        button = Button(frame, text='%+d' % i, command=increment)
        button.pack()
    return label
     

Python语法没有提供表明名称得分的方法   增量中提到的是指变量得分   make_scoreboard,不是增量中的局部变量。用户和   Python的开发人员表示有兴趣删除它   限制使Python可以具有完全的灵活性   Algol风格的范围模型现在是许多编程中的标准   语言,包括JavaScript,Perl,Ruby,Scheme,Smalltalk,C with   GNU扩展和C#2.0。

答案 1 :(得分:2)

Constantinius给出的理由是正确的。处理它的另一种方法(不使用全局变量)将是

def foo(a):
    def bar(a):
        a -= 1
        return a
    return bar(a)
>>> print foo(5) 
4