了解可变参数时的函数作用域 [英] Understanding function scopes when it comes to mutables

查看:36
本文介绍了了解可变参数时的函数作用域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For some reasons, Python is able to access mutables in the scope of an inner function if the mutable is modified in-place (or is not modified at all).

这同样不适用于不可变对象,除非它们被声明为 nonlocal,参见 f7f8.

The same does not apply to immutables, unless they are declared as nonlocal, see f7 and f8.

使用任何不会就地修改变量的运算符修改可变变量会导致 UnboundLocalError 异常.

Modifying a mutable with any operator that does not modify the variale in-place causes an UnboundLocalError exception.

但是,我在 Python 文档中找不到任何特定于此行为的信息(或者我无法识别此信息).

However, I could not find anything specific to this behaviour in the Python docs (or I was unable to identify this information).

我一直在看的:

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

对我来说有意义的是:

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

When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the block’s environment. [...]

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

If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.

我认为这解释了为什么 f1f3 起作用,而 f2f4 不起作用,但是,我仍然不明白为什么 f6 不起作用 - 毕竟 c[0] = c[0] 的使用发生在 c = c 之前.

I think this explains why f1 and f3 work, while f2 and f4 don't, however, I'm still confused as to why f6 does not work - after all the use of c[0] = c[0] happens before c = c.

所以我的问题是:

为什么 f6 不起作用?以后使用 c = c 是否以某种方式遮蔽"了外部作用域?

Why does f6 not work? Is the later use of c = c somehow "shadowing" the outer scope?

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

If it is, where is this behaviour described in the docs?

如果您能参考该行为的官方文档来支持您的回答,我将不胜感激.

I would really appreciate if you could back up your answer with a reference to an official documentation of the behaviour.

这里有一些函数可以重现我的发现:

Here are some functions to reproduce my findings:

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()()

推荐答案

您似乎忽略了您引用的文档的一部分.重点是:

You seem to have ignored a part of the documentation you quoted. Here is is with added emphasis:

如果名称绑定操作发生在代码块内的任何地方,则块内名称的所有使用都被视为对当前块的引用.

If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block.

在您的示例中,代码块"是内部函数,名称绑定操作"是 c = c 赋值.分配是否为空操作并不重要.当您在函数的任何位置对名称 c 进行赋值时(无论分配了什么值),编译器都会注意到 c 是一个局部变量,因此 c 的>所有使用(即使是那些更早出现的)将被视为本地引用.

In your example, the "code block" is the inner function and the "name binding operation" is the c = c assignment. It doesn't matter that the assignment is a no-op. When you do an assignment to the name c anywhere in the function (regardless of what value is assigned), the compiler will note that c is a local variable and so all uses of c (even those that occur earlier) will be treated as local references.

这篇关于了解可变参数时的函数作用域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆