为啥在函数和类中处理未绑定的局部变量有区别?

Posted

技术标签:

【中文标题】为啥在函数和类中处理未绑定的局部变量有区别?【英文标题】:Why the difference in handling unbound locals in functions versus classes?为什么在函数和类中处理未绑定的局部变量有区别? 【发布时间】:2018-07-16 06:10:36 【问题描述】:

当引用全局变量时,可以看到函数和类以不同的方式处理这一点。第一个很好,第二个导致错误:

x = 10
class Foo():
    x = x + 1
a = foo()

对比:

x = 10
def faa():
    x = x + 1
faa()

在Python execution model中是这样描述的:

类定义是可以使用和定义的可执行语句 名字。这些引用遵循名称解析的正常规则 除了未绑定的局部变量在 全局命名空间。

但是为什么呢?

我遇到的唯一其他提示是this bit:

类的套件然后在一个新的执行框架中执行(见 部分命名和绑定),使用新创建的本地命名空间和 原始的全局命名空间。 (通常,该套件仅包含 函数定义。)当类的套件完成执行时,它的 执行帧被丢弃,但其本地命名空间被保存。 4一个 然后使用基类的继承列表创建类对象 属性字典的类和保存的本地命名空间。

这仍然没有解释为什么这会导致在全局命名空间中查找未绑定的本地人。

这两个链接都来自this answer,虽然没有详细说明原因。

【问题讨论】:

我不这么认为,至少我的问题在那里没有得到回答。该问题涉及公共和私人,并且在此范围内广泛制定。 如果您对如何澄清许多问题有任何建议,但我会很高兴。 参见here: " 类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的常规规则,但在全局中查找未绑定的局部变量除外命名空间。”以及***.com/questions/291978/… 上的更多信息(此处为 Python3 答案:***.com/a/23471004/92092) @pindakaas,尤其是查看this 答案而不是接受的答案,以了解 Python 范围规则中类主体范围的特殊性质。 请参阅this question,了解类主体范围的另一个令人惊讶的方面,其中涉及很多细节。从这个极好的答案中得到的一个结论是:“因为作用域被重新用作类对象的属性,所以允许它被用作一个作用域也会导致未定义的行为...... Python 必须以不同的方式对待一个类作用域,因为它非常不同于函数范围。” 【参考方案1】:

如Python FAQ - Why am I getting an UnboundLocalError?中所述

... 因为当您对作用域中的变量进行赋值时,该变量将成为该作用域的本地变量,并隐藏外部作用域中任何类似命名的变量。由于 foo 中的最后一条语句为 x 分配了一个新值,因此编译器将其识别为局部变量。

因此,当早先尝试访问未初始化的局部变量时,会导致错误。

这解释了原因:

x = 10
def foo():
    x = x+1  # raises UnboundLocalError
foo()

引发异常,但不是这样:

x = 10
def faa():
    y = x+1  # x stays in global scope, since it is not assigned in local
faa()

class foo() 也是一样,因为 python 允许在任何给定时间将属性分配给对象。类代码将x 分配为对象a 的新属性。

x = 10
class foo():
    x = x+1  # scope of x: class
a = foo()  # x = 10; a.x = foo.x = 11

等同于:

x = 10
class foo():
    def __init__(self):
        self.x = x+1  # scope of x: instance
a = foo()  # x = 10; a.x = 11

显然self.x 被分配而不是x,因此也保持在全局范围内。 (另见here)

【讨论】:

对不起,这只是重申我已经知道的。你也可能想看看 foo.x 这是一个 foo 的局部变量,它隐藏了全局变量 x。所以没有 a.x 没有分配给 a 的本地空间。 a 在 foo 中查找。 python 确实将 a.x 分配为 11!因此没有使用local x,因为目标是类的局部空间,因此python再次在全局空间中寻找未知值。在您的函数中,您在为其分配值时将 x 分配为 local,因为您不使用任何关键字,导致 python 在本地空间中使用它,而不是从全局中隐藏它。如果您将 x+1 分配给不同的变量,该函数将起作用。感谢您快速投反对票。 不是我。但是再看一下 a.__dict__ 你会发现里面没有 x 在 foo.x 中查找它。 这很有趣,print(a.x) 显示为 11,而 a.__dict__ 显示为空(在 python2.7 和 3.6 中测试) @pindakaas 这篇文章真的很有帮助,我根据这篇文章的见解重新编写了答案

以上是关于为啥在函数和类中处理未绑定的局部变量有区别?的主要内容,如果未能解决你的问题,请参考以下文章

全局变量和局部变量的区别

JS全局变量是全局对象的属性,函数局部变量为啥就不是函数的属性呢?

Java 对象和类

成员变量类变量局部变量的区别

java变量

JAVA-初步认识-第六章-成员变量和局部变量的区别