Python:理解引用
我理解基本的python引用,比如a+=b和a=a+b之间的区别,但这让我感到困惑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 这是怎么
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.7base开始
一直深入到原始阵列。从发行说明中:
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]
@HenryGomersallarr1[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