当内部函数将值分配给局部变量时,为什么会发生UnboundLocalError

时间:2019-04-18 05:04:19

标签: python

我在外部函数中编写一个内部函数,然后发生了一些连接,即在向内部函数中的局部变量赋值时,发生了UnBoundLocalError。相反,如果我只是在内部函数中打印出局部变量,它就可以很好地工作。让我向您展示简化的代码。我知道这与Python中的LEGB规则有关,但是我仍然找不到原因。如果有人可以给我一些见解,我会非常感激。让我向您展示简化代码。


def outer1():
    number = 10
    def inner():
        print(number)
    inner()

def outer2():
    number = 20
    def inner():
        if number >= 20:
            number += 1
    inner()

outer1()函数运行良好,但是outer2()函数抛出了UnboundLocalError: local variable 'number' referenced before assignment

我知道使用nonlocal关键字可以解决此问题。但是还是有一些让我困惑的东西。为什么if函数中的outer2()语句没有像number函数那样在外部函数中查找变量outer1()。有人可以给我一些解释吗?

2 个答案:

答案 0 :(得分:1)

在这种情况下,您需要使用nonlocal语句:

def outer2():
    number = 20
    def inner():
        nonlocal number
        if number >= 20:
            number += 1
    inner()

要了解原因,请看一下字节码:

import dis


def outer2():
    number = 20
    def inner():
        if number >= 20:
            number
    inner()



def outer3():
    number = 20
    def inner():
        if number >= 20:
            number = number + 1
    inner()


def outer4():
    number = 20
    def inner():
        nonlocal number
        if number >= 20:
            number = number + 1
    inner()

>>> dis.dis(outer2)
  2           0 LOAD_CONST               1 (20)
              3 STORE_DEREF              0 (number)

  3           6 LOAD_CLOSURE             0 (number)
              9 BUILD_TUPLE              1
             12 LOAD_CONST               2 (<code object inner at 0x7ff003b56b70, file "<stdin>", line 3>)
             15 LOAD_CONST               3 ('outer2.<locals>.inner')
             18 MAKE_CLOSURE             0
             21 STORE_FAST               0 (inner)

  6          24 LOAD_FAST                0 (inner)
             27 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             30 POP_TOP
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE


>>> dis.dis(outer3)
  2           0 LOAD_CONST               1 (20)
              3 STORE_FAST               0 (number)

  3           6 LOAD_CONST               2 (<code object inner at 0x7ff003b56ae0, file "<stdin>", line 3>)
              9 LOAD_CONST               3 ('outer3.<locals>.inner')
             12 MAKE_FUNCTION            0
             15 STORE_FAST               1 (inner)

  6          18 LOAD_FAST                1 (inner)
             21 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             24 POP_TOP
             25 LOAD_CONST               0 (None)
             28 RETURN_VALUE


>>> dis.dis(outer4)
  2           0 LOAD_CONST               1 (20)
              3 STORE_DEREF              0 (number)

  3           6 LOAD_CLOSURE             0 (number)
              9 BUILD_TUPLE              1
             12 LOAD_CONST               2 (<code object inner at 0x7ff003af7e40, file "<stdin>", line 3>)
             15 LOAD_CONST               3 ('outer4.<locals>.inner')
             18 MAKE_CLOSURE             0
             21 STORE_FAST               0 (inner)

  7          24 LOAD_FAST                0 (inner)
             27 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             30 POP_TOP
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE

从这些示例中可以看到,尝试分配给变量(未声明为非本地变量) 产生MAKE_FUNCTION操作码。但是只有闭包允许将访问变量移出当前范围。 您还可以阅读有关闭包here的更多信息。

注意:这已在Python 3.5中进行了测试;另请参阅有关在Python 3.6中将changes转换为MAKE_FUNCTIONMAKE_CLOSURE操作码的信息

答案 1 :(得分:0)

def outer2():
  number = 20
  def inner():
     nonlocal number # this is the additional change required
     if number >= 20:
         number += 1
  inner()

在python函数中,可以访问函数甚至方法中的“全局”变量,但不能为它们分配值,因为那样的话,该函数将寻找变量的本地副本。 错误的原因是该函数在其符号表条目中查找局部变量“ number”,但找不到它。

这通常可以通过使用global关键字来指定您正在编辑的变量具有全局范围来解决,但是由于该函数是在另一个函数中定义的,因此您将需要使用'nonlocal'关键字,这是另一种方法说要修改的变量不是该函数的局部变量,而是定义它的函数的局部变量,在本例中为external2。