更改列表的元素与更改列表本身

时间:2015-08-04 04:05:42

标签: python variables scope

我有一个关于变量名称范围的问题,我试图在这里找到答案,我得到了一个类似的但是在这里仍然有点困惑 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

为什么我不需要添加“全局位置”来改变变量位置的元素?

3 个答案:

答案 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对象模型
  • 作用域

首先是范围规则:

以下是变量范围规则的层次结构

  1. 本地范围 - 功能内部
  2. 封闭 - 封闭功能中的任何一个或全部
  3. 全球 - 顶级模块(文件)
  4. 内置 - 任何正在导入的BuiltIn模块
  5. Python对象模型:

    在Python中,一切都是对象。 当一个变量被声明为x = 1000时,会发生以下步骤

    • 创建值为1000的int对象
    • 创建了对象引用x
    • x指向整数对象

    现在,当在position[0] = x内执行语句tick()时,该语句不具有仅初始化部分的赋值。由于position未在函数范围内定义,因此它按照范围规则的层次结构的顺序查找 - 封闭范围(父函数),后跟模块范围(文件),后跟内置模块。在这里,它在全局范围内找到变量并更新它。

    如果您的语句为position = [100, 100],则会创建一个新的列表对象,并在tick()函数的范围内创建新的对象引用并执行赋值

    如果你有global position语句,它告诉Python全局范围中的变量与Local Scope中的变量相同。然后position = [100,100]创建一个列表对象[100,100],并将其全局变量position链接到新的列表对象。