【问题标题】:Why do immutable objects throw UnboundLocalError but mutable ones do not?为什么不可变对象会抛出 UnboundLocalError 而可变对象不会?
【发布时间】:2015-12-03 00:51:57
【问题描述】:

在尝试将 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 无需定义其全局范围即可继续正常工作。

这是为什么呢?

【问题讨论】:

标签: python scope


【解决方案1】:

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

如果您尝试执行x_id_list += [1],您会遇到同样的错误。它是分配给一个变量,而不是改变一个,这就是这里的问题。

来自the docs

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

虽然一开始有点令人惊讶,但稍加思考就可以解释这一点。一方面,要求global 分配变量提供了防止意外副作用的障碍。另一方面,如果所有全局引用都需要global,那么您将一直使用global。您必须将每个对内置函数或导入模块组件的引用声明为全局。这种混乱会破坏global 声明在识别副作用方面的作用。

This answer 还介绍了更多细节。

【讨论】:

  • 关于“改变 x_id_list 所指的列表不会改变 x_id_list 的值”的要点。一些参考会很好。
【解决方案2】:

这是因为全局作用域变量在函数中是只读的。

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

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

【讨论】:

  • 您是否认为 x_id_list 不是只读但 x_id 是?还需要参考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-05
  • 2021-04-18
  • 2019-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多