我有一个关于变量名称范围的问题,我试图在这里找到答案,我得到了一个类似的但是在这里仍然有点困惑 Function changes list values and not variable values in Python
所以代码如下:
position = [50, 50]
# Handler for timer
def tick():
x = random.randrange(0, width)
y = random.randrange(0, height)
position[0] = x
position[1] = y
为什么我不需要添加“全局位置”来改变变量位置的元素?
答案 0 :(得分:1)
只有在分配全局位置变量以引用新列表(没有全局声明,)时,才需要添加global position
position 将写入函数的locals。
在您的情况下,您所做的只是查找全局变量 position 以查找它所引用的列表。然后你就地更新了这个列表。
这是一个显示所有变体的Python tutor visualization(分配给本地,分配给全局,更新全局引用的列表)。
另外,看看Ned Batchelder的nice blog post,它完整地解释了python的传递对象机制。
答案 1 :(得分:1)
来自documentation of Naming and Binding -
如果在代码块中使用了变量但未定义,则它是一个自由变量。
来自documentation of global statement -
全局语句是一个声明,它包含整个当前代码块。这意味着列出的标识符将被解释为全局变量。如果没有全局变量,则无法分配给全局变量,虽然自由变量可以引用全局变量而不被声明为全局变量。
(强调我的)
当你这样做时 -
position[0] = <something>
您不是定义任何内容,您正在改变已经定义的列表 position
inplace(更改其第0个元素以引用其他内容)。
让我们假设,如果没有定义position
,那么当你执行类似的事情时
position[0] = 1
您将获得NameError
-
>>> position[0] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'position' is not defined
这就是为什么,即使你做postion[0] = <something>
,它仍然是一个自由变量,因为它没有在块中定义,因此它被视为全局。
更新:
更多信息,当你这样做时 -
position[0] = <something>
这不是simple assignment
statement,实际上是augmented assignment statement。
答案 2 :(得分:1)
您需要应用两个概念来理解您的代码
首先是范围规则:
以下是变量范围规则的层次结构
Python对象模型:
在Python中,一切都是对象。
当一个变量被声明为x = 1000
时,会发生以下步骤
x
x
指向整数对象现在,当在position[0] = x
内执行语句tick()
时,该语句不具有仅初始化部分的赋值。由于position
未在函数范围内定义,因此它按照范围规则的层次结构的顺序查找 - 封闭范围(父函数),后跟模块范围(文件),后跟内置模块。在这里,它在全局范围内找到变量并更新它。
如果您的语句为position = [100, 100]
,则会创建一个新的列表对象,并在tick()函数的范围内创建新的对象引用并执行赋值
如果你有global position
语句,它告诉Python全局范围中的变量与Local Scope中的变量相同。然后position = [100,100]
创建一个列表对象[100,100]
,并将其全局变量position
链接到新的列表对象。