为什么不可变对象抛出UnboundLocalError但是可变对象不抛出?

时间:2015-12-03 00:51:58

标签: python scope

在尝试为类实例分配GUID的代码时,我写了类似于以下内容的内容:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
x_id = 0 # immutable
x_id_list = [0] # mutable
def fx(x):
    global x_id # Disable to get "UnboundLocalError: local variable 'x_id' referenced before assignment"
    if x is None:
        x_id += 2
        x_id_list[0] += 2
    else:
        x_id += 1
        x_id_list[0] += 1
        return (x_id - 1)
    return x_id

expected = [x for x in xrange(10)]
actual = [fx(x) for x in expected]
assert(expected == actual), "expected = {}, actual = {}".format(expected, actual)
print x_id_list
print x_id

请注意,如果未定义全局作用域,则只有不可变x_id会抛出UnboundLocalError,但是可变x_id_list继续正常工作,而不需要定义其全局作用域。 / p>

为什么?

2 个答案:

答案 0 :(得分:0)

问题不在于x_id是不可变的(它不是 - the integer value 0 is what's immutable),而是您无法通过{明确声明您的意图而无法分配给函数外部定义的变量{1}}。变更列表global是指不会更改x_id_list变量本身的值,因此是允许的。

如果您尝试x_id_list,则会遇到同样的错误。它分配给变量,而不是改变,这就是问题所在。

来自the docs

  

在Python中,仅在函数内引用的变量是隐式全局变量。如果在函数体内的任何位置为变量赋值,则除非明确声明为全局,否则将其视为局部值。

     

虽然起初有点令人惊讶,但片刻的考虑解释了这一点。一方面,对指定的变量要求x_id_list += [1]可以防止意外的副作用。另一方面,如果所有全局引用都需要global,那么您将一直使用global。您必须将对内置函数或导入模块的组件的每个引用声明为全局。这种混乱会破坏global声明用于识别副作用的有用性。

This answer也会详细介绍。

答案 1 :(得分:-1)

那是因为全局范围变量在函数中是只读的。

由于您正在修改变量x_id,因此您需要在使用它之前引用它,否则Python将尝试创建一个本地函数的新变量。

在函数开头使用了global表达式,您已告诉Python您需要在函数中引用和修改该变量。由于您正在修改变量x_id,因此会发生这种情况。由于x_id_list是可变的,因此您基本上不会明显地修改它(仅在内部,与x_id不同,此类更改是外部的),因此,您不需要global } keyword。