为什么 python 对变量有这种行为?

Why does python behave this way with variables?

我一直试图在下面的代码块中理解为什么 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,并且我知道显式比隐式好。 :) 这是真的。否则代码会混乱并导致错误。 那个问题只是关于我试图理解的一些无用且不切实际的大学理论。

Edit_3: 现在我完全理解它为什么会发生以及这里发生了什么。感谢 Randall Valenciano for providing this link to a blog 解释得很好。

函数是作为一个整体来解释的,而不是逐行解释的。因此,当解释函数时,任何已定义变量的变量声明都将移至函数的顶部。因此,当我们打印 'var' 时,该函数正在使用尚未为其分配任何值的局部声明变量,然后解释器会抱怨它并抛出错误。

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

您的 var 被定义为全局变量。在每个函数中,当您只读取 var 时,您正在访问全局变量,但是当函数中某处有一个赋值给 var 时,python 对待 every 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 的评论 - 我错过了你原来的问题。

How could I use a predefined global variable in a function, then define a local variable with the same name inside of the same function, without changing the value of the global variable ? ( in python of course )

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

这是 this answer

的 Python 范围解析的规则

LEGB 规则。

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

E, Enclosing-function locals — 在局部范围内命名任何和所有静态封闭函数(def 或 lambda),从内到外。

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

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

所以基本上在你的问题中,范围解析是 "from inside out" 并且由于你没有使用 global 关键字,解释器不知道在局部函数范围之外寻找该变量 var。所有解释器看到的是您在声明和定义变量之前正在使用它,从而引发错误。全局变量通常是危险的,因此 Python 想通过强迫您明确说明来确保您知道要使用全局变量。

有关 global 关键字的解释,请参见 this other answer

希望对您有所帮助。