【问题标题】:Precise rules of variable binding in nested scopes [duplicate]嵌套范围内变量绑定的精确规则
【发布时间】:2020-12-06 20:36:00
【问题描述】:

我似乎对 Python 变量绑定有误解。给定具有阴影名称的嵌套范围,确定访问哪个变量的精确规则是什么?

让我用一些例子来说明。首先是基本阴影。

a = 1

def foo():
    a = 2
    def _foo():
        return a
    return _foo()

print(foo())  # -> 2

这里一切都很好。该值被覆盖并相应地返回。但是,如果在函数定义之后改变了值,它仍然是内部值:

def bar():
    def _bar():
        return a
    a = 2
    return _bar()

print(bar())  # -> 2

此外,定义一个引用不存在变量的函数是可能的。

def baz():
    def _baz():
        return b
    return _baz()

那么,如果后面定义了b,就可以执行函数了。 如果在另一个内部范围中定义则不行:

def qux(f):
    b = 3
    return f()

print(qux(baz()))  # -> NameError

现在,所有这些情况都可以通过让 Python 了解程序后面的行来解释,但这与我对 Python 是一种解释性语言、逐行推进的知识相冲突。那么语句是一次解析而不是逐行解析吗?

一个带有阴影类属性的奇怪行为让我更加反感。

class C:
    a = 2
    b = a

    def meth(self):
        return a

    c = meth

print(C.b, C().meth(), C.c)  # -> 2 1 C.meth

这里a被定义为一个类属性,并在b中成功使用,但这并没有延续到方法定义中。方法本身可以在后面的属性中使用,但不能在其他方法中使用,例如不经过self

我对绑定同时发生的猜测是否正确?在那种情况下,类主体是设计上的例外,还是根本不是范围?还是这里发生了其他事情?

【问题讨论】:

  • stackoverflow.com/questions/291978/… 的前两个答案应该可以回答您的问题。请注意,“封闭”是指“在内部定义”,而不是“被调用”。
  • @ThierryLathuille 非常感谢!

标签: python scope


【解决方案1】:

我想你可能想多了。

默认情况下,变量在创建时被放在最窄的封闭函数范围内。

来自所有封闭范围的变量以只读容量提供,无论是封闭函数的范围还是全局范围。如果您尝试分配给它,它将在最窄的封闭范围内创建一个新变量,从而隐藏那些外部。使用 global 关键字将外部变量带入本地范围将阻止这种情况发生,允许您将内容分配给非本地范围。

此外,请记住,函数是在解释 def 语句时编译和评估的。对于嵌套函数,本质上,每个新调用都会重新评估内部函数。这也意味着内部函数对外部函数的范围具有只读访问权限。和往常一样的规则。

您的bar() 示例有效,因为当python 尝试访问变量a 时,它至少存在于一个封闭范围中。 Python 直到最后一刻才检查这些东西。您的 qux() 示例不起作用,因为声明 b 的范围未包含定义 _baz() 的范围,因此无法访问。


类范围很奇怪。评估类时,其中定义的所有变量都绑定到class。但是,对于包含在其中的方法而言,该类并不能真正算作它自己的范围。将meth() 视为一个未绑定函数,在全局范围内声明,C.meth 指的是(现在是C.c)。通过点表示法调用函数是一种语法简写:

# the following two are identical
C().meth()
C.meth(C())

虽然C.meth 在技术上绑定到C,但它并未包含在C 的类级命名空间中。尝试执行 C().meth() 将失败,因为未针对函数定义 a。 (请注意,如果a 定义在全局范围内,则该函数将按预期工作 - C.meth() 将全局范围作为父级,而不是 C 的类级范围)。

【讨论】:

  • 非常感谢!所以这证实了我的猜想。我觉得这有点奇怪,尤其是 class body 案例,但很高兴知道。尽管这仅在少数情况下真正相关。干杯!
猜你喜欢
  • 2013-11-21
  • 2015-08-15
  • 1970-01-01
  • 2017-07-29
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
相关资源
最近更新 更多