可变性、局部性和循环
Mutability, Locality, and Looping
我有一些来自初学者编码练习的代码:
numbers = []
i = 0
def populate(maximum, step):
while i < maximum:
numbers.append(i)
i = i + step
populate(10, 2)
堆栈跟踪失败:
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
是一个整数,因此它是不可变的,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 循环作为代码块的产物吗?
这段代码在正确的地方失败了:
def populate(maximum, step):
while i < maximum:
raise Exception("foo")
堆栈跟踪:
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 语言中的 nothing 专门处理可变值(然而,这是一个常见的神话)。
关键的见解是名称的范围在每个范围内都是固定的。在 populate
的范围内, 每个 名称必须是本地的或全局的:此决定是方法字节码的一部分。
可以从封闭范围中查找只能读取的名称。但是,对范围内任何位置的名称的赋值会强制将其视为范围内 everywhere 的局部变量。 (除非您使用 global
或 nonlocal
关键字。)
因此,如果您在方法中将 i
anywhere 赋值,则 i
必须是该方法的局部新变量,而不是全球 i
。如果您希望 i
表示全局 i
,只需在方法顶部添加此行:
global i
我有一些来自初学者编码练习的代码:
numbers = []
i = 0
def populate(maximum, step):
while i < maximum:
numbers.append(i)
i = i + step
populate(10, 2)
堆栈跟踪失败:
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
是一个整数,因此它是不可变的,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 循环作为代码块的产物吗?
这段代码在正确的地方失败了:
def populate(maximum, step):
while i < maximum:
raise Exception("foo")
堆栈跟踪:
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 语言中的 nothing 专门处理可变值(然而,这是一个常见的神话)。
关键的见解是名称的范围在每个范围内都是固定的。在 populate
的范围内, 每个 名称必须是本地的或全局的:此决定是方法字节码的一部分。
可以从封闭范围中查找只能读取的名称。但是,对范围内任何位置的名称的赋值会强制将其视为范围内 everywhere 的局部变量。 (除非您使用 global
或 nonlocal
关键字。)
因此,如果您在方法中将 i
anywhere 赋值,则 i
必须是该方法的局部新变量,而不是全球 i
。如果您希望 i
表示全局 i
,只需在方法顶部添加此行:
global i