首页 > 解决方案 > 如何调用 np.array 的值而不是内存地址?

问题描述

在下面的代码中,我打算通过附加(随机)numpy 数组来创建一个从空列表开始的列表。对于一个临时变量,我初始化了一个 numpy 数组变量“sample_pt”,它作为一个临时变量来保存一个(随机)numpy 数组。虽然我希望有一个随机 numpy 数组的列表,但输出是一个填充了相同(最终)numpy 数组的列表。我怀疑通过“变量名”调用一个 numpy 数组会返回它的内存地址。我的方向是否正确,或者有什么值得了解的吗?

[代码]

import numpy as np

sample_pt=np.array([0.]) # initial point
sample_list=[]
number_iter=3

for _ in range(number_iter):
    sample_pt[0]=np.random.randn()
    sample_list.append(sample_pt)
    print(sample_list)

[输出]

[array([-0.78614157])]
[array([0.7172035]), array([0.7172035])]
[array([0.47565398]), array([0.47565398]), array([0.47565398])]

标签: pythonarraysnumpy

解决方案


我不知道您所说的“调用值”或“而不是内存地址”是什么意思,或者……您问题的大部分内容。

但问题很简单。您一遍又一遍地附加相同的数组,而不是创建新数组。

如果你想创建一个新数组,你必须明确地这样做。这是微不足道的;只需将np.array构造函数移动到循环中,如下所示:

sample_list=[]
number_iter=3

for _ in range(number_iter):
    sample_pt=np.array([0.]) # initial point
    sample_pt[0]=np.random.randn()
    sample_list.append(sample_pt)
    print(sample_list)

但这可以大大简化。

首先,与其创建一个包含 1 个零的数组,然后替换那个零,为什么不直接创建一个包含您想要的元素的数组呢?

sample_pt = np.array([np.random.randn()])

或者,更好的是,为什么不让我们np.random为您构建阵列呢?

sample_pt = np.random.randn(1)

此时您可以用列表理解替换整个内容:

number_iter = 3
sample_list = [np.random.randn(1) for _ in range(number_iter)]

或者,更好的是,为什么不创建一个 3x1 数组而不是 3 个单元素数组的列表呢?

number_iter = 3
sample_array = np.random.randn((number_iter, 1))

如果您出于某种原因确实需要将其更改为 3 个数组的列表,您可以list稍后随时调用它:

sample_list = list(sample_array)

……或一开始:

sample_list = list(np.random.randn((number_iter, 1)))

同时,我认为您误解了值和变量在 Python 中的工作方式。

首先,暂时忘记“内存地址”:

  • 对象是堆中某处的具有类型的值。你不在乎在哪里。
  • 变量没有内存地址或类型;它们只是某个命名空间中的名称(全局变量、局部变量、某个实例的属性等),它们在某处引用某个值。

请注意,这与 C++ 非常不同,C++ 中的变量是类型化的内存位置,而对象位于这些内存位置中。这意味着 Python 中没有“复制构造函数”或“赋值运算符”或类似的东西。当您编写a = b时,这意味着它a现在是与 . 相同值的另一个名称b。如果你想要一份副本,你必须明确要求一份副本。

现在,如果你看看 CPython 是如何在幕后实现的:

  • CPython 解释器将所有对象表示为指向PyObject结构的指针,这些结构总是在堆上分配。
  • 变量只是 a 中的字符串键dict,由模块(对于全局变量)、实例(对于属性)或其他任何东西拥有。中的值dict只是像其他任何对象一样的对象。这意味着,在幕后,实际上存储在哈希表中的是指向键中变量名称的字符串对象的指针,以及指向您在值中分配的任何值的指针。
  • 本地有一个特殊的优化,涉及存储在框架上的对象指针数组,但您通常不必担心这一点。
  • 闭包捕获还有另一个特殊技巧,涉及指向包含指向实际对象的指针的单元对象的指针,您不必经常担心。

正如你所看到的,考虑指针更难理解,并且可能会产生误导,除非你真的关心 CPython 是如何在幕后工作的。


推荐阅读