Python:理解引用

Python:理解引用,python,numpy,Python,Numpy,我理解基本的python引用,比如a+=b和a=a+b之间的区别,但这让我感到困惑 import numpy as np arr1 = np.arange(6).reshape(2,3) arr2 = arr1[0] arr2 is arr1[0] #returns False, when I expect True arr1[0] = [7,8,9] arr2 #[7,8,9], when I expect [0,1,2] since the 'is' returned False 这是怎么

我理解基本的python引用,比如a+=b和a=a+b之间的区别,但这让我感到困惑

import numpy as np
arr1 = np.arange(6).reshape(2,3)
arr2 = arr1[0]
arr2 is arr1[0] #returns False, when I expect True
arr1[0] = [7,8,9]
arr2 #[7,8,9], when I expect [0,1,2] since the 'is' returned False

这是怎么回事?

您混淆了
=
=
运算符

  • is
    进行身份检查。使用它可以查看两个变量是否实际指向同一对象,而不仅仅是具有相同的值
  • =
    是赋值运算符。它为变量(或切片,如您的情况)赋值
  • =
    是相等运算符。它检查两个对象是否具有相同的语义值
因此,在你的情况下:

arr2 == arr[0] # now should return True

从数组中读取值时,Numpy似乎会创建新对象。因此,每次阅读时,都会创建一个新对象。因此,两次读取生成的对象不相同,因此,
is
会给出
False

为numpy数组编制索引时,会创建一个新视图(它本身就是一个numpy数组)。这是一个不同的对象,因此
失败的,但它是硬件内存中同一块内存的视图。因此,当您修改该视图时,您将修改可能存在另一个视图的内存位

编辑:通过检查数组的
ctypes.data
属性,实际上可以看到与numpy数组关联的内存的起始地址

In [1]: import numpy as np

In [2]: arr1 = np.arange(6).reshape(2,3)

In [3]: arr2 = arr1[0]

In [4]: arr2.ctypes.data
Out[4]: 39390224

In [5]: arr1[0].ctypes.data
Out[5]: 39390224
同样的

>>> arr1 = np.arange(6).reshape(2,3)
>>> arr1
array([[0, 1, 2],
       [3, 4, 5]])
>>> arr2 = arr1[0]
>>> arr2
array([0, 1, 2])
所以是的,
arr1[0]
arr2
是相等的。但是,

>>> arr2 is arr1[0]
False
因为它不是比较nArray的工具。使用
is
,检查
arr1[0]
arr2
是否为同一对象,而不是同一对象。尝试使用
==
,您将获得

>>> arr2 == arr1[0]
array([ True,  True,  True], dtype=bool)
或者使用
numpy.equal(t1,t2)

然后,您希望对等式测试有一个布尔答案,请执行以下操作:

>>> (np.equal(arr2, arr1[0])).all()
True

如果需要检查两个numpy数组是否指向相同的数据,请使用
base
属性。从你的例子来看:

>>> arr1 = np.arange(6).reshape(2,3)
>>> arr2 = arr1[0]
>>> arr1
array([[0, 1, 2],
       [3, 4, 5]])
>>> arr1.base # arr1 is a view of the array before reshaping!
array([0, 1, 2, 3, 4, 5])
>>> arr2.base
array([[0, 1, 2],
       [3, 4, 5]])
>>> arr2.base is arr1
True
从numpy 1.7
base开始
一直深入到原始阵列。从发行说明中:

Ndarray上的
.base
属性,用于视图以确保 现在,拥有内存的底层阵列不会过早释放 拥有“视图对视图”时折叠参照。例如:

在numpy 1.6中,
c.base
b
c.base.base
a
。在numpy 1.7中,
c.base
a


“is”运算符比较两个对象的标识;函数的作用是:返回一个表示其标识的整数(当前实现为其地址)

比如,

a = 1, b = 1
a is b
Out[26]:True

id(a)
Out[27]:37470472L

id(b)
Out[27]:37470472L

id(1)
Out[27]:37470472L
这三个人都有相同的身份。但是,

a = [1]
b = [1]
a is b
Out[26]:False

id(a)
Out[37]:142781064L

id(b)
Out[38]:142780616L

因此,在数组中也会发生同样的事情。他们没有相同的身份。

这似乎是努比的一些有趣的阴谋。正常的列表会像你期望的那样工作。这是一种要求。Numpy数组在概念上只是内存块。可能会将实际整数值存储在相同的内存位中,但当不可变的int通过写入
arr1[0]
@HenryGomersall
arr1[0]
arr2
而发生变异时,就会造成大破坏。@delnan是的,我刚刚注意到(干杯!)。。。所以
arr1[0]
是一个内存位视图,而不是内存位本身。执行
arr1[0]
操作时,将创建相同内存位的新视图。这意味着
is
失败,但您可以通过每个视图修改相同的内存块。这并不能解释为什么
arr2
稍后在分配给
arr1
时会发生更改。这可能是正确的,但我们可以获得一些详细信息吗?例如,实际执行此操作的numpy代码块?我似乎无法在Source中找到它,但这就是numpy数组——定义良好的内存块。本质上,numpy是一个非常低级的pythonic接口。我说的是视图的创建-这些不只是Python对象吗?@voithos当然。你的问题是为什么相同内存的新视图是不同的对象?这是唯一明智的做法。numpy的工作并不是缓存所有创建的视图,以免用户再次想要相同的精确对象,但缺乏将其写入代码的远见。只是澄清最后一条评论。将创建的数组分配给新变量意味着新变量是同一对象。因此
arr3=arr1
后跟
arr3是arr1
将返回
True
。当创建新视图(通过索引现有数组)时,就会创建一个新对象。有趣的是,实际上可以通过检查对象的
id()
来愚弄自己,就像我昨天在玩这个问题时发生的一样。如果检查视图的id(使用
id(arr1[0])
),然后分配
arr2=arr1[0]
,并检查其id(使用
id(arr2)
),则这些id很可能是相同的。创建了
arr1[0]
,找到了它的id,然后它立即超出范围,因此可以将内存(这是CPython分配id的方式)重新分配给新对象。我认为您应该只比较作用域中对象的ID。
a = 1, b = 1
a is b
Out[26]:True

id(a)
Out[27]:37470472L

id(b)
Out[27]:37470472L

id(1)
Out[27]:37470472L
a = [1]
b = [1]
a is b
Out[26]:False

id(a)
Out[37]:142781064L

id(b)
Out[38]:142780616L