为啥 python 对变量有这种行为?

Posted

技术标签:

【中文标题】为啥 python 对变量有这种行为?【英文标题】:Why does python behave this way with variables?为什么 python 对变量有这种行为? 【发布时间】:2018-06-19 21:51:27 【问题描述】:

在下面的代码块中,我一直试图理解为什么 python 会这样。我已经完成了我的研究,但找不到一个好的答案,所以我来这里看看是否有人能指出我正确的方向或提供一个很好的澄清。 我知道这与一些旧的 ALGOL 原则有关,但我并不完全理解。

var = 5

def func1():
    print(var)
func1()

def func2():
    var = 8
    print(var)
func2()

def func3():
    print(var)
    var = 8
func3()

这段代码的输出如下:

5 8 UnboundLocalError:赋值前引用了局部变量“var”

我明白为什么我们会得到输出“5”和“8”。但是对于'func3()',我期待输出'5'。看起来,解释器认为我想在函数中打印本地“var”而不是全局“var”。所以它会抛出这个错误。

或者,如果在函数内部的某个地方定义了一个变量,那么该函数将默认为局部变量,而不是同名的全局变量。

但是为什么 python 会这样呢?我不是在抱怨,我只是想了解一些事情......

如何在函数中使用预定义的全局变量,然后在同一函数中定义同名的局部变量,而不更改全局变量的值? (当然是在 python 中)

在此先感谢大家。你们是了不起的人! :)

Edit_1:感谢大家的精彩回答。我完全理解在函数中使用预定义的全局变量,然后在同一个函数中定义一个同名的局部变量是一个糟糕且不切实际的想法。我只是从理论的角度考虑它,因为我在大学讲座中看到了它。 XD 我找不到一个用例,最好这样做!

Edit_2:我已经阅读了 PEP8,并且我知道显式比隐式更好。 :) 这是真的。否则代码会令人困惑并导致错误。 这个问题只是关于我试图理解的一些无用和不切实际的大学理论。

编辑_3: 现在我完全理解它为什么会发生以及这里发生了什么。感谢Randall Valenciano 提供此链接到blog,它很好地解释了它。

发生的情况是函数被解释为一个整体,而不是逐行解释。因此,当函数被解释时,任何已定义变量的变量声明都被移到函数的顶部。因此,当我们打印“var”时,该函数正在使用尚未分配任何值的本地声明的变量,然后解释器会抱怨它并抛出错误。

再次感谢大家! :) 你对我帮助很大!现在我终于明白幕后发生了什么。

【问题讨论】:

和JS提升是一样的行为,这里有一个快速简洁的解释:foreigngods.com/blog/2013/09/14/26/… 是的,如果在分配给它的代码块中的任何位置,编译器会将其标记为本地。我们认为您不能使用全局变量然后在同一块中创建具有相同名称的局部变量。为什么你会想要?这听起来是个坏主意。 相关:***.com/q/370357/1639625 但我认为这实际上是在问一个不同的问题。 你可以定义def func3(var=var):;这会将局部参数var 分配给全局var 的值,而func3 内部对var 的所有赋值都转到局部变量,但是在定义函数时会这样做,所以局部@987654331调用函数时,@ 可能没有全局 var 的当前值。又是一个问题:你为什么要这个? 【参考方案1】:

您的var 被定义为一个全局变量。在每个函数中,当您只读取 var 时,您正在访问全局变量,但是当函数中某处为 var 赋值时,python 会处理 每个 var在函数中作为局部变量。因此,为什么您的最后一个函数失败了,因为在分配 var = 8 之前调用了 print(var)(本地变量)。

您可以在以下主题中了解更多信息:How bad is shadowing names defined in outer scopes?Python nonlocal statement

最好的办法是明确您的代码,这样在您尝试引用本地、非本地或全局变量时就不会感到困惑了。

在这种情况下,假设您打算继续使用全局变量,请执行以下操作:

var = 5

def func1():
    print(var)
func1()

def func2():
    global var
    var = 8
    print(var)
func2()

def func3():
    global var
    print(var)
    var = 8  # technically this is not necessary any more var = 8 was already assigned when func2() is called
func3()

输出如下:

5
8
8

编辑:感谢juanpa.arrivillaga 的评论 - 我错过了你原来的问题。

如何在函数中使用预定义的全局变量,然后 在相同的内部定义一个具有相同名称的局部变量 函数,而不改变全局变量的值? ( 在 当然是python)

简短的回答是 - 首先定义本地 var,就像在 func2() 中所做的那样,你很好。更长的答案是 - 你为什么要这样做?当您在不同范围内具有相同名称的变量时,它会造成混乱并令人头疼。更好的方法是将您的本地 var 命名为 local_var 或其他名称,以便它明显不同且易于追踪。

【讨论】:

这会改变func3中的全局值 是的,但他在func3() 中的var = 8 行我假设这就是意图。 "如何在函数中使用预定义的全局变量,然后在同一个函数内部定义一个同名的局部变量,不改变全局变量的值?" 啊,我完全失明了。感谢那。 IMO 的答案应该是“他们不应该那样做”,但我会更新我的答案。【参考方案2】:

这是来自this answer 的 Python 范围解析规则

LEGB 规则。

L, Local — 在函数中以任何方式分配的名称(def 或 lambda)),并且未在该函数中声明为全局的。

E, Enclosure-function locals - 在任何和所有静态封闭函数(def 或 lambda)的本地范围内的名称,从内部到外部。

G, Global (module) — 在模块文件的顶层分配的名称,或通过在文件中的 def 中执行全局语句来分配。

B,内置 (Python) — 在内置名称模块中预分配的名称:open,range,SyntaxError,...

所以基本上在您的问题中,范围解析是“从内到外”,并且由于您没有使用 global 关键字,解释器不知道在本地函数范围之外查找该变量 @987654324 @。解释器所看到的只是您在声明和定义变量之前使用了一个变量,从而引发了错误。全局变量通常很危险,因此 Python 希望通过强制您明确说明来确保您知道要使用全局变量。

有关 global 关键字的说明,请参阅 this other answer

希望对你有帮助。

【讨论】:

我已经明白全局变量的作用了。还有python作用域解析的概念。 :) 我只是想了解 python 在这种特殊情况下的行为。至于为什么它在函数内部使用局部变量而不是全局变量。为什么它不只在定义后的行中使用局部变量,而在之前的行中使用全局变量。 :)

以上是关于为啥 python 对变量有这种行为?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 1+++2 = 3?

为啥我不能在使用 Javascript 的 if 语句中重新分配这个变量? [复制]

为啥一个变量(随机数)的行为就像一个“发电机”

为啥使用python查询数据库数据没有返回也没有报错

python中的列表赋值行为

无名变量声明 - 为啥它起作用?