【问题标题】:populating lists -- what is python doing?填充列表——python在做什么?
【发布时间】:2014-10-05 04:29:00
【问题描述】:

我认为我已经解决了我自己的问题,但我正在寻求更好地理解原因,或者得到启发/纠正。

我有一个我称之为 vec 的列表:

vec = [0.0, 0.0] 

在读入数据时改变值。

为了比较当前值和以前的值,我有另一个列表,我称之为 oldvec。 如果我将 oldvec 定义为

oldvec = vec

然后每次 vec 更改值时它都会更改值,因此比较是无用的——它们总是相同的。

但是,如果我改为写

oldvec = [vv for vv in vec] 

我没有这个问题——即使 vec 发生变化,oldvec 也会保持其值,因此当前向量和先前向量之间的比较可以按我的需要进行,即它实际上检测到重复和非重复! ...为什么?

【问题讨论】:

标签: python list


【解决方案1】:

查看幕后情况的一种方法是使用id 函数。这显示了一个对象的内存地址。内存地址是指对象在物理内存中的存储位置。

如果我们为这三个命令运行它,并查看不同的地址(这只是在我的电脑上;如果你自己运行它会得到不同的数字):

>>> vec = [0.0, 0.0]
>>> print id(vec)
4501729936

>>> oldvec1 = vec
>>> print id(oldvec1)
4501729936

>>> oldvec2 = [vv for vv in vec]
>>> print id(oldvec2)
4502046984

我们看到vecoldvec1 指的是同一个地址,所以它们是同一个对象的两个不同标签。在底层,Python 正在处理地址为4501729936 的对象:变量名称vecoldvec1 只是我们使用的方便标签。它们不是指“不同”的对象。

相比之下,oldvec2 则完全不同。当 Python 运行列表推导时,它不知道这会发生与以前相同的列表,因此它会创建该列表的新副本。


这是一个快速的、肮脏的图片来展示正在发生的事情。尽管红色 blob 和绿色 blog 恰好包含相同的信息,但它们是两个不同的 blob。 vecoldvec1 都指向同一个红色 blob,因此对其中任何一个的任何操作都会影响底层的红色 blob,并反映在另一个上。相比之下,oldvec2 指向一个完全不同的绿色 blob,它恰好是红色 blob 中信息的副本,但对绿色 blob 的更改不会影响红色 blob。

【讨论】:

    【解决方案2】:

    将 oldvec 设置为 vec 确实使 oldvec 指向 vec。你还没有创建一个新列表,你只是为它取了另一个名字。通过使用列表推导,您可以显式创建列表的新副本,相当于 vec.copy()。

    【讨论】:

      【解决方案3】:

      在 Python 中,变量是“引用”,这意味着您可以有两个变量引用同一个对象。在您的第一个示例中,正在发生的事情是:同一个列表有两个名称。

      如果您需要第二个实际列表,您可以“复制”第一个。具体方法见这里:How to clone or copy a list?

      请注意,这也适用于列表中的项目——如果它们是更复杂的对象,您可以选择对列表进行“深度复制”,复制每个元素的每个部分,或者“浅拷贝”,只复制引用,所以你会有一个包含对原始对象的新引用的新列表。您需要为每个用例选择正确的方法。

      【讨论】:

        【解决方案4】:

        您应该将 Python 列表视为一个对象:在内存中某处实例化的东西,用一个或多个指针存储其内存地址 - 即,当您说 [] 时,您正在内存中的某处分配一些新空间。因此,当您调用vec = [0.0, 0.0] 时,它会在内存中的某处创建一个新列表,并且它的地址 存储在vec 变量中。因此,当您执行oldvec = vec 时,您只是将地址从vec 复制到oldvec

        让我用一个例子来说明:例如,假设您的列表[0.0, 0.0] 存储在地址0x0800。当你说vec = [0.0, 0.0] 时,vec 变量现在接收到0x0800。当您说oldvec = vec 时,oldvec 收到相同的0x0800。因此,当您访问oldvec 的第一个元素时,您确实在访问vec 指向的same 列表。

        现在,想想你的新线路:oldvec = [vv for vv in vec]。当您执行[] 时,它会在内存中的其他位置创建一个新列表,对吗?正如for 命令所说,这个列表填充了vec 的元素。因此,它会在内存中的其他位置创建一个新列表,存储0.00.0(以及稍后的新元素,如果我理解正确的话)。这就是 Python 在内部处理您的命令的方式。

        希望对您有所帮助。

        【讨论】:

          猜你喜欢
          • 2010-10-16
          • 1970-01-01
          • 2020-01-16
          • 1970-01-01
          • 2011-12-04
          • 1970-01-01
          • 2014-08-01
          • 2016-10-06
          • 2020-10-29
          相关资源
          最近更新 更多