Python 内部方法无法从外部方法获取变量

Python inner method cannot get variable from outer method

代码如下:

def cycle(f1, f2, f3):
    """Returns a function that is itself a higher-order function.

    >>> def add1(x):
    ...     return x + 1
    >>> def times2(x):
    ...     return x * 2
    >>> def add3(x):
    ...     return x + 3
    >>> my_cycle = cycle(add1, times2, add3)
    >>> identity = my_cycle(0)
    >>> identity(5)
    5
    >>> add_one_then_double = my_cycle(2)
    >>> add_one_then_double(1)
    4
    >>> do_all_functions = my_cycle(3)
    >>> do_all_functions(2)
    9
    >>> do_more_than_a_cycle = my_cycle(4)
    >>> do_more_than_a_cycle(2)
    10
    >>> do_two_cycles = my_cycle(6)
    >>> do_two_cycles(1)
    19
    """
    "*** YOUR CODE HERE ***"
    def execution(n):
        def inner(x):
            result = x
            while (n > 0):
                n = n - 1
                if (n >= 0):
                    result = f1(result)
                n = n - 1
                if (n >= 0):
                    result = f2(result)
                n = n - 1
                if (n >= 0):
                    result = f3(result)
            return result
        return inner
    return execution

运行 在终端测试

$ python -m doctest xx.py

得到错误:

UnboundLocalError: local variable 'n' referenced before assignment

我认为方法inner中使用的n变量可以从外部方法execution中获取,因为在运行时,execution必须是第一次调用是调用getinner方法,所以变量n肯定已经初始化了。我在这里犯了什么错误?我用的是python 3.8.1

发生了什么事,为什么?基于变量范围,您希望在外部范围(如果未定义)中查找变量并且它确实有效,试试这个:

def o(n):
    def i():
        print("now in inner", n)
    print("passed to outer", n)
    i()

o(42)

现在如果你在一个内部作用域中定义一个同名变量,它将(在那个作用域中)隐藏在外部作用域中的变量,你可以给它赋任何值,你可以访问它,这一切在其范围内发生:

def o(n):
    def i():
        n = "in"
        print("now in inner", n)
    print("passed to outer", n)
    i()
    print("back in outer", n)

o(42)

那你为什么看到 UnboundLocalError 异常?因为 python 将考虑在该范围内分配给它的范围内的任何变量,如果在此类分配之前引用它,则不会在外部范围内访问它,而是认为尚未在本地分配:

def o(n):
    def i():
        print("now in inner", n)
        n = "in"
    print("passed to outer", n)
    i()
    print("back in outer", n)

o(42)

如果您按照评论中的建议添加 nonlocal 语句,错误就会消失,因为变量正在访问封闭范围而不是被视为本地,但这也意味着任何更改/(重新)分配也在影响外部范围,并且(尤其是在较大的程序中)可能会令人惊讶和困惑:

def o(n):
    def i():
        nonlocal n
        print("now in inner", n)
        n = "in"
    print("passed to outer", n)
    i()
    print("back in outer", n)

o(42)

为了使相关位更紧密地联系在一起并明确关系,将变量的值传递给内部函数可能更好,执行它要执行的任何逻辑并让它 return 结果:

def o(n):
    def i(inner_n):
        print("now in inner", n)
        inner_n += 1
        return inner_n
    print("passed to outer", n)
    print("i() returned", i(n))
    print("back in outer", n)

o(42)

指向documentation:

The following constructs bind names: ... targets that are identifiers if occurring in an assignment ...

If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global.

或者如前所述,赋值可能是该块中的最后一个动作,但这意味着变量在整个过程中都被视为本地变量(除非明确告知不要这样做)。