如何扁平化一个memoryview?

5

我有一个 memoryview,其步幅不是很规则,就像下面这样:

>>> mv.strides
(96, 32, 8)

我想将这个memoryview对象写入一个socket,但我的网络库似乎只支持mv.strides == (1,)memoryview。在Python中是否有一种方法可以使这个memoryview变平(flatten)?

>>> flatten(mv).strides
(1,)

理想情况下,这不会影响底层字节也不需要复制。我可以使用NumPy来完成这个任务,但如果可能的话,我更喜欢保持通用性。
编辑:以下是一些生成此类memoryview的代码。
In [1]: import numpy as np
In [2]: x = np.ones((2, 3, 4))
In [3]: x.data
Out[3]: <memory at 0x7f371aa849a8>

In [4]: x.data.strides
Out[4]: (96, 32, 8)

这个 memoryview 的源头是什么?那不是基本的 Python 类或概念。我只知道 cython 有这个术语。 - hpaulj
@hpaulj 这是一个 Python 概念: memoryview. 但是了解我们所讨论的确切的 memoryview 是有好处的。 - MSeifert
Memoryviews 确实是 Python 的一部分。它们通常被数值计算社区广泛使用。我的用户经常将它们作为 numpy 计算的结果提供,但我更希望有一个不以 numpy 为中心的解决方案。 - MRocklin
如果您不想要一个以numpy为中心的答案,请添加使用Python内置的东西创建一个的代码。 - martineau
1个回答

5

仅为澄清,您可能已经知道这一点,但我认为最好确保:

  • 步幅元组的长度表示维数的数量,因此(1, )(8, )都是一维的,而(10, 2)(20, 1)都是二维的。
  • 对于C连续数组,步幅元组中的最后一个元素表示内存视图中项目大小。这并不总是正确的:有时值会填充,那么它将比实际项目大小更大 - 但在大多数情况下,它代表项目大小。

因此,您不仅想要将内存视图展平,而且应该将其展平并具有项目大小为1

在Python 3.3中,添加了memoryview.cast方法,使得展平数组变得轻而易举:

cast(format[, shape])

将一个内存视图转换为新的格式或形状。shape默认为[byte_length//new_itemsize],这意味着结果视图将是一维的。返回值是一个新的内存视图,但缓冲区本身不会被复制。支持的转换为1D -> C连续和C连续 -> 1D。

目标格式限制为struct语法中的单个元素本地格式。其中一个格式必须是字节格式('B'、'b'或'c')。结果的字节长度必须与原始长度相同。

因此,只有在转换为char (c)、unsigned char (B)或signed chars (b)并且它是C连续时才有效。

>>> import numpy as np

>>> memview = memoryview(np.ones((2, 3, 4)))
>>> memview.cast('b').strides   # or 'B' or 'c'
(1, )

然而,它被压缩并解释为1字节值。如果您只想将其压平,则需要再次将其转换为原始类型:
>>> memview.cast('b').cast(memview.format)

这将是一维的,但它不会有步幅为(1, ),因为浮点数占用8个字节(至少如果是float64):

>>> memview.cast('b').cast(memview.format).strides
(8, )

1
一系列的 cast 调用适用于非字节格式:x.data.cast('B').cast('B',shape=[192]).cast('d')。但是 'd' 的步幅为 (8,)。因此,步幅为 (1,) 将需要一个兼容的格式。 - hpaulj
@hpaulj 噢,没错,但是使用 .cast('B').cast(memview.format) 更容易。这样就不需要指定形状了。 - MSeifert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接