代码之家  ›  专栏  ›  技术社区  ›  Mike Scotty

当涉及可变项时,理解函数作用域

  •  2
  • Mike Scotty  · 技术社区  · 7 年前

    出于某些原因,如果可变表被就地修改(或根本没有修改),Python能够访问内部函数范围内的可变表。

    nonlocal 看见 f7 f8 .

    UnboundLocalError 例外

    然而,我在Python文档中找不到任何特定于这种行为的内容(或者我无法识别这些信息)。

    我一直在看的是:

    https://docs.python.org/3.6/tutorial/classes.html#python-scopes-and-namespaces https://docs.python.org/3.6/reference/executionmodel.html#resolution-of-names

    当在代码块中使用名称时,将使用最近的封闭范围进行解析。代码块可见的所有此类作用域的集合称为块环境。 [...]

    如果名称绑定操作发生在代码块内的任何位置,则块内名称的所有使用都被视为对当前块的引用。在绑定块之前在块内使用名称时,这可能会导致错误。这条规则很微妙。Python缺少声明,允许名称绑定操作在代码块中的任何位置发生。通过扫描代码块的整个文本进行名称绑定操作,可以确定代码块的局部变量。

    f1 f3 工作,而 f2 f4 f6 不起作用-毕竟使用了 c[0] = c[0] 发生在之前 c = c

    为什么 不工作?是后面使用的 不知何故“遮蔽”了外部范围?

    如果是,文档中描述的这种行为在哪里?

    以下是一些重现我的发现的函数:

    import inspect
    
    def f1():
        c = [0]
        def g():
            print(inspect.currentframe().f_code.co_freevars) # ('c',)
            c[0] = c[0]
            return c
        return g
    f1()()
    
    def f2():
        c = [0]
        def g():
            print(inspect.currentframe().f_code.co_freevars) # ()
            c = c # UnboundLocalError: local variable 'c' referenced before assignment
            return c
        return g
    # f2()()
    
    def f3():
        c = [0]
        def g():
            print(inspect.currentframe().f_code.co_freevars) # ('c',)
            c.append(1)
            return c
        return g
    f3()()
    
    def f4():
        c = [0]
        def g():
            print(inspect.currentframe().f_code.co_freevars) # ()
            c = c[0] # UnboundLocalError: local variable 'c' referenced before assignment
            return c
        return g
    # f4()()
    
    def f5():
        c = [0]
        def g():
            nonlocal c
            print(inspect.currentframe().f_code.co_freevars) # ('c',)
            c = c
            return c
        return g
    f5()()
    
    def f6():
        c = [0]
        def g():
            print(inspect.currentframe().f_code.co_freevars) # ()
            c[0] = c[0] # UnboundLocalError: local variable 'c' referenced before assignment
            c = c
            return c
        return g
    f6()()
    
    def f7():
        c = 1
        def g():
            print(inspect.currentframe().f_code.co_freevars) # ()
            c = c # UnboundLocalError: local variable 'c' referenced before assignment
            return c
        return g
    # f7()()
    
    def f8():
        c = 1
        def g():
            nonlocal c
            print(inspect.currentframe().f_code.co_freevars) # ('c',)
            c = c
            return c
        return g
    f8()()
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Blckknght    7 年前

    您似乎忽略了您引用的文档的一部分。以下是附加强调:

    代码块内的任何位置 ,块内名称的所有使用都被视为对当前块的引用。

    在您的示例中,“代码块”是内部函数,“名称绑定操作”是 c = c c 在函数中的任何位置(无论分配了什么值),编译器都会注意到 c 全部的 c