关于python:Mutability,Locality和Looping

Mutability, Locality, and Looping

本问题已经有最佳答案,请猛点这里访问。

我有一些来自初学者编码练习的代码:

1
2
3
4
5
6
7
8
9
numbers = []
i = 0

def populate(maximum, step):
    while i < maximum:
        numbers.append(i)
        i = i + step

populate(10, 2)

堆栈跟踪失败:

1
2
3
4
5
6
Traceback (most recent call last):
    File"test_python.py", line 9, in <module>
        populate(10, 2)
    File"test_python.py", line 5, in populate
        while i < maximum:
UnboundLocalError: local variable 'i' referenced before assignment

这是迄今为止我对这个问题的理解…

  • i是一个int,因此是不变的,numbers是一个列表,是可变的。
  • 由于i是不可变的,因此它可以在超出范围时读取。但是,当它不在范围内(即在populate函数中)时重写它会导致UnboundLocalError
  • 由于numbers是一个可变的列表,附加在它之后不会导致覆盖,因此不会导致UnboundLocalError的出现。
  • 如果对populate方法进行了简单的更改,程序将成功运行(由于i未被覆盖)

    def populate(maximum, step):
    new_i = i
    while new_i < maximum: numbers.append(i) new_i = new_i + step

  • 如果我注释掉i = i + 1行,while循环(显然)将永远运行,但程序不会失败。

我的问题是,为什么python在第5行碰到while循环时会失败,而不是在第7行遇到实际问题(i = i + 1时)?这是解释器将while循环作为代码块的一个工件吗?

这段代码在正确的位置失败:

1
2
3
def populate(maximum, step):
    while i < maximum:
        raise Exception("foo")

堆栈跟踪:

1
2
3
4
5
6
Traceback (most recent call last):
    File"test_python.py", line 12, in <module>
        populate(10, 2)
    File"test_python.py", line 6, in populate
        raise Exception("foo")
Exception: foo

另一个注意事项:只有当变量在控制块的开始部分使用时(即while i < maximum)才会出现这种情况。每种类型的控制块都会发生同样的行为:while、for、if、elif等等。


易变性在这里是一条红鲱鱼。可变值受作用域影响的方式与不可变值相同。实际上,在Python语言中,没有任何东西专门处理可变的值(然而,这是一个常见的神话)。

关键的洞察是,名称的作用域在每个作用域中都是固定的。在populate的范围内,每个名称必须是本地的或全局的:这个决定是方法字节码的一部分。

只能从封闭范围中查找只读名称。但是,对作用域内任何位置的名称的赋值会强制将其作为作用域内任何位置的局部变量来处理。(除非使用globalnonlocal关键字。)

因此,如果您在方法的任何地方给i赋值,那么i必须是方法局部的新变量,而不是全局i变量。如果希望i表示全局i,只需在方法的顶部添加这一行:

1
global i