考虑这个数组:
In [1]: a = numpy.array([[1,2],[3,4]], dtype=numpy.uint8)
In [2]: a.strides
Out[2]: (2, 1)
In [3]: a.flat[:]
Out[3]: array([1, 2, 3, 4], dtype=uint8)
In [4]: a.flags['C_CONTIGUOUS']
Out[4]: True
In [5]: numpy.getbuffer(a)[:]
Out[5]: 'x01x02x03x04'
目前为止,一切都好。 但是,请注意,当我创建该数组的视图时会发生什么,其中插入大小为 1 的维度:
In [6]: b = a[:, numpy.newaxis, :] # Insert dimension
In [7]: b.strides
Out[7]: (2, 0, 1)
In [8]: b.flat[:]
Out[8]: array([1, 2, 3, 4], dtype=uint8)
In [9]: b.flags['C_CONTIGUOUS']
Out[9]: False
In [10]: numpy.getbuffer(b)[:]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/.../<ipython-input-28-0127b71fae43> in <module>()
----> 1 numpy.getbuffer(b)[:]
TypeError: single-segment buffer object expected
什么给? 为什么numpy
认为b
不C_CONTIGUOUS
? 肯定是吧? 还是我错过了什么?
更新:@senderle指出numpy.reshape()
按预期工作:
In [11]: b = numpy.reshape(a, (2,1,2))
In [12]: b.flags['C_CONTIGUOUS']
Out[12]: True
这很奇怪,我本来希望两种情况下的观点都是相同的。
这有点神秘,在看到hpaulj的评论之前,我什至深入研究了源代码。他观察到reshape
和切片会产生不同的步幅newaxis
显示了导致这种行为的原因 - 回答为什么问题。
但仍然存在一个问题,即是什么促使了这种行为——为什么问题。我没有任何确凿的证据证明这一点,但我的直觉是,numpy
尽其所能快速确保切片是视图,而不是副本,在这种情况下,这需要打破 c 连续性。问题在于,可能很难确定如何将正确的步幅维度添加到具有奇怪步幅的数组中;在某些情况下甚至可能是不可能的。但是,您始终可以将步幅0
的维度添加到任何数组。因此,numpy
不是特殊的外壳,检查c连续性和其他可能的步幅安排,而是简单地增加了步幅0
维度。
这种懒惰是有道理的,因为它更简单,并且(可能)因为它更快(尽管对 c 连续性的单次检查不会花费很多时间)。我认为简单解释在这里优先 - 在这种情况下,事情会很快变得非常复杂。
另一方面,reshape
需要能够生成任意形状的数组,因此它尊重连续性要求,并在必要时进行复制。