比较NumPy对象引用

2024-05-03 02:38:44 发布

您现在位置:Python中文网/ 问答频道 /正文

我想了解裸体的行为。在

当我试图获取NumPy数组的内部数组的引用,然后将其与对象本身进行比较时,我得到的是返回值False。在

示例如下:

In [198]: x = np.array([[1,2,3], [4,5,6]])
In [201]: x0 = x[0]
In [202]: x0 is x[0]
Out[202]: False

另一方面,对于Python本机对象,返回的是True。在

^{pr2}$

我的问题是,那是纽比的故意行为吗?如果是这样,如果我想创建NumPy数组内部对象的引用,我应该怎么做。在


Tags: 对象innumpyfalsetrue示例isnp
3条回答

现在不确定它是否有用,但numpy.ndarray.ctypes似乎有有用的位: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html

用了这样的东西(缺少dtype,但是meh):

def is_same_array(a, b):
    return (a.shape == b.shape) and (a == b).all() and a.ctypes.data == b.ctypes.data

这里: https://github.com/EricCousineau-TRI/repro/blob/a60daf899e9726daf2ca1259bb80ad2c7c9b3e3f/python/namedlist_alt.py#L111

二维切片

当我第一次写这篇文章时,我构建了一个一维数组并编制了索引。但是OP使用的是2d数组,所以x[0]是一个“行”,是原始数组的一部分。在

In [81]: arr = np.array([[1,2,3], [4,5,6]])
In [82]: arr.__array_interface__['data']
Out[82]: (181595128, False)

In [83]: x0 = arr[0,:]
In [84]: x0.__array_interface__['data']
Out[84]: (181595128, False)        # same databuffer pointer
In [85]: id(x0)
Out[85]: 2886887088
In [86]: x1 = arr[0,:]             # another slice, different id
In [87]: x1.__array_interface__['data']
Out[87]: (181595128, False)
In [88]: id(x1)
Out[88]: 2886888888

我之前写的关于切片的文章仍然适用。为单个元素建立索引,如arr[0,0]的工作原理与1d数组相同。在

这个2d arr与1darr.ravel()具有相同的数据缓冲区;形状和跨距是不同的。而且viewcopy和{}之间的区别仍然适用。在

在C中实现2d数组的一种常见方法是使用指向其他数组的指针数组。numpy采用不同的strided方法,只使用一个平面数据数组,并使用shape和{}参数来实现交叉。因此子数组需要它自己的shape和{}以及指向共享数据缓冲区的指针。在

一维阵列索引

我将尝试演示索引数组时发生的情况:

^{pr2}$

数组是一个具有各种属性的对象,例如形状和数据缓冲区。缓冲区将数据存储为字节(在C数组中),而不是Python数字对象。您可以通过以下方式查看有关阵列的信息:

In [52]: np.info(arr)
class:  ndarray
shape:  (4,)
strides:  (4,)
itemsize:  4
aligned:  True
contiguous:  True
fortran:  True
data pointer: 0xa84f8d8
byteorder:  little
byteswap:  False
type: int32

或者

In [53]: arr.__array_interface__
Out[53]: 
{'data': (176486616, False),
 'descr': [('', '<i4')],
 'shape': (4,),
 'strides': None,
 'typestr': '<i4',
 'version': 3}

一个是十六进制的数据指针,另一个是十进制的。我们通常不会直接引用它。在

如果我索引一个元素,我会得到一个新对象:

In [54]: x1 = arr[1]
In [55]: type(x1)
Out[55]: numpy.int32
In [56]: x1.__array_interface__
Out[56]: 
{'__ref': array(1),
 'data': (181158400, False),
....}
In [57]: id(x1)
Out[57]: 2946170352

它有数组的一些属性,但不是全部。例如,您不能分配给它。还要注意,它的“data”值是完全不同的。在

从同一个地方进行另一个选择-不同的id和不同的数据:

In [58]: x2 = arr[1]
In [59]: id(x2)
Out[59]: 2946170336
In [60]: x2.__array_interface__['data']
Out[60]: (181143288, False)

另外,如果此时更改阵列,则不会影响之前的选择:

In [61]: arr[1] = 10
In [62]: arr
Out[62]: array([ 0, 10,  2,  3])
In [63]: x1
Out[63]: 1

x1x2没有相同的id,因此与is不匹配,它们也不使用arr数据缓冲区。没有记录表明这两个变量都是从arr派生的。在

使用slicing可以得到原始数组的view

In [64]: y = arr[1:2]
In [65]: y.__array_interface__
Out[65]: 
{'data': (176486620, False),
 'descr': [('', '<i4')],
 'shape': (1,),
 ....}
In [66]: y
Out[66]: array([10])
In [67]: y[0]=4
In [68]: arr
Out[68]: array([0, 4, 2, 3])
In [69]: x1
Out[69]: 1

它的数据指针比arr大4个字节,也就是说,它指向同一个缓冲区,只是一个不同的点。而改变y确实改变了arr(但不是独立的x1)。在

我甚至可以查看此项目的0d视图

In [71]: z = y.reshape(())
In [72]: z
Out[72]: array(4)
In [73]: z[...]=0
In [74]: arr
Out[74]: array([0, 0, 2, 3])

在Python代码中,我们通常不处理这样的对象。当我们使用c-api或{}时,是否可以直接访问数据缓冲区。nditer是一种迭代机制,可以处理像这样的0d对象(在Python或c-api中)。In cythontyped memoryviews对于低级访问特别有用。在

http://cython.readthedocs.io/en/latest/src/userguide/memoryviews.html

https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html

https://docs.scipy.org/doc/numpy/reference/c-api.iterator.html#c.NpyIter

元素方面==

作为对评论的回应,Comparing NumPy object references

np.array([1]) == np.array([2]) will return array([False], dtype=bool)

==是为数组定义的元素操作。它比较各个元素的值并返回匹配的布尔数组。在

如果这种比较需要在标量上下文中使用(例如if),则需要将其缩减为单个值,如np.all或{}。在

is测试比较对象id(不仅仅是numpy对象)。它在实际编码中的应用价值有限。我经常在is None之类的表达式中使用它,其中None是一个具有唯一id的对象,它不能很好地用于等式测试。在

我想你对Numpy数组的理解有点欠缺。您认为Numpy中多维数组中的子数组(就像Python列表中的子数组)是独立的对象,其实它们不是。在

一个Numpy数组,不管它的维数是多少,都只是一个对象。这是因为Numpy在C级别创建数组,当将它们作为python对象加载时,不能将其分解为多个对象。这使得Python在使用诸如split()__getitem__take()等属性时,创建一个新对象来保存新的部分,事实上,这正是Python为Numpy数组抽象类似于列表的行为的方式。在

您还可以实时检查精简,如下所示:

In [7]: x
Out[7]: 
array([[1, 2, 3],
       [4, 5, 6]])

In [8]: x[0] is x[0]
Out[8]: False

所以一旦你有了一个数组或者任何一个可以容纳其他对象的可变对象,你就会有一个python可变对象,因此你将失去Numpy数组的性能和所有其他优秀特性。在

另外,正如@Imanol在评论中提到的,如果您想在使用引用修改数组时有一个内存优化和灵活的操作,那么您可能需要使用Numpy视图对象。view可以通过以下两种方式构造对象:

a.view(some_dtype) or a.view(dtype=some_dtype) constructs a view of the array’s memory with a different data-type. This can cause a reinterpretation of the bytes of memory.

a.view(ndarray_subclass) or a.view(type=ndarray_subclass) just returns an instance of ndarray_subclass that looks at the same array (same shape, dtype, etc.) This does not cause a reinterpretation of the memory.

For a.view(some_dtype), if some_dtype has a different number of bytes per entry than the previous dtype (for example, converting a regular array to a structured array), then the behavior of the view cannot be predicted just from the superficial appearance of a (shown by print(a)). It also depends on exactly how a is stored in memory. Therefore if a is C-ordered versus fortran-ordered, versus defined as a slice or transpose, etc., the view may give different results.

相关问题 更多 >