【问题标题】:Python throws UnboundLocalError when a global variable is used in the conditional of an if statement within a function当在函数内的 if 语句的条件中使用全局变量时,Python 会抛出 UnboundLocalError
【发布时间】:2019-01-18 15:27:58
【问题描述】:

尽管在全局范围内声明了变量,但我收到了 UnboundLocalError。似乎该变量被假定/强制为该特定语句的局部变量。

我不明白为什么 if 语句条件中的变量会被区别对待。有人可以解释发生了什么吗?

以下 sn-p 是我尝试做的一个简化示例:

In [1]:  global mock
...      mock = False
...
...
...      def foo():
...          if not mock:
...              # do stuff
...              mock = True
...
...          # do other stuff
...
...
...      foo()

这会引发 UnboundLocalError,尽管在函数声明的正上方声明了全局 bool。

---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-31-540737067cb1> in <module>()
      7 
      8 
----> 9 foo()

<ipython-input-31-540737067cb1> in foo()
      3 
      4 def foo():
----> 5     if not mock:
      6         mock = True
      7 

UnboundLocalError: local variable 'mock' referenced before assignment

但是,这个 sn-p 按预期运行:

In [2]:  mock = False
...
...
...      def bar():
...          return mock
...
...
...      bar()

Out[2]:  False

同样,这个在 if 语句体中带有变量的 sn-p 也可以按预期运行:

In [3]:  mock = False
...
...
...      def foobar():
...          if True:
...              return mock
...
...          return True
...
...
...      foobar()

Out[3]:  False

我在文档 (https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces) 中找到了以下关于范围和命名空间的文章,但我认为它应该根据以下内容按预期工作,

虽然范围是静态确定的,但它们是动态使用的。在执行过程中的任何时候,至少有三个嵌套作用域的命名空间是可以直接访问的:

  • 最里面的范围,首先搜索,包含本地名称
  • 从最近的封闭范围开始搜索的任何封闭函数的范围都包含非本地名称,但也包含非全局名称
  • 倒数第二个作用域包含当前模块的全局名称
  • 最外层范围(最后搜索)是包含内置名称的命名空间

我在 Windows 10 上的 Conda 中使用 Jupyter 4.4.0 和 Python 3.6.6 和 IPython 6.5.0,但我能够使用控制台 IPython 和运行 Python 3.6.0 的 IDLE 3.6.0 GUI 重现此行为。

编辑:不知何故,以下 sn-p 不受影响?似乎这与更新同一变量中的变量有关。我觉得我现在一定错过了一些微不足道的事情。

In [4]:  global mock # also works without this line
...      mock = False
...
...
...      def foo1():
...          if not mock:
...              return mock
...
...          return True
...
...
...      foo1()

Out[4]:  False

【问题讨论】:

    标签: python python-3.x scope


    【解决方案1】:

    变量是函数范围的。因为函数中有一个对名称 mock 的赋值,所以对 mock 的所有引用都被尝试为“本地”(它们实际上被编译为不同的操作码!)

    您可以通过在函数中使用global mock 来更改此设置(因此分配和引用被强制为全局

    def foo():
        global mock
        if not mock:
            mock = true
    

    请注意,在您的第一个示例中,您在模块范围内编写了 global mock - 这没有任何作用:)

    直接看反汇编可能更有意义:

    >>> import dis
    >>> def f():
    ...     if not mock:
    ...         mock = True
    ...
    >>> dis.dis(f)
      2           0 LOAD_FAST                0 (mock)
                  3 POP_JUMP_IF_TRUE        15
    
      3           6 LOAD_GLOBAL              0 (True)
                  9 STORE_FAST               0 (mock)
                 12 JUMP_FORWARD             0 (to 15)
            >>   15 LOAD_CONST               0 (None)
                 18 RETURN_VALUE
    >>> def g():
    ...     global mock
    ...     if not mock:
    ...         mock = True
    ...
    >>> dis.dis(g)
      3           0 LOAD_GLOBAL              0 (mock)
                  3 POP_JUMP_IF_TRUE        15
    
      4           6 LOAD_GLOBAL              1 (True)
                  9 STORE_GLOBAL             0 (mock)
                 12 JUMP_FORWARD             0 (to 15)
            >>   15 LOAD_CONST               0 (None)
                 18 RETURN_VALUE
    

    【讨论】:

    • 如果您发现此答案有用或解决方案,请单击绿色复选标记。
    猜你喜欢
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 2013-02-28
    • 1970-01-01
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多