Python:截断字节缓冲区时为零拷贝

Python:截断字节缓冲区时为零拷贝,python,Python,这是一个关于Python的noob问题 Python中有没有一种方法可以从bytearray的开头截短几个字节,并在不将内容复制到另一个内存位置的情况下实现这一点?以下是我正在做的事情: inbuffer = bytearray() inbuffer.extend(someincomingbytedata) x = inbuffer[0:10] del inbuffer[0:10] 我需要保留被截断的字节(由x引用)并对其执行一些操作 x是否会指向与inbuffer[0]相同的内存位置,或者上

这是一个关于Python的noob问题

Python中有没有一种方法可以从bytearray的开头截短几个字节,并在不将内容复制到另一个内存位置的情况下实现这一点?以下是我正在做的事情:

inbuffer = bytearray()
inbuffer.extend(someincomingbytedata)
x = inbuffer[0:10]
del inbuffer[0:10]
我需要保留被截断的字节(由x引用)并对其执行一些操作

x是否会指向与inbuffer[0]相同的内存位置,或者上述代码中的第三行是否会复制数据。另外,如果没有复制,最后一行中的删除是否也会删除x引用的数据?由于x仍在引用该数据,GC不应回收该数据。是这样吗

编辑:


如果这不是在不复制的情况下截断字节缓冲区并返回截断字节的正确方法,是否有其他类型的缓冲区安全地支持这种操作?

很容易检查:

>>> inbuffer = bytearray([1, 2, 3, 4, 5])
>>> x = inbuffer[0:2]
>>> print id(x) == id(inbuffer)
False
所以它不是同一个对象

您还询问了指向缓冲区[0]的
x
。你好像误解了什么。Python中的数组与C中的数组的工作方式不同。
inbuffer
的地址不是
inbuffer[0]
的地址:

>>> inbuffer = bytearray([1, 2, 3, 4, 5])
>>> print id(inbuffer) == id(inbuffer[0])
False
这些是围绕C级阵列的包装

同样在Python中,一切都是对象。Python将所有整数缓存到256(范围为
bytearray
)。因此,唯一被复制的是指针:

>>> inbuffer = bytearray([1, 2, 3, 4, 5])
>>> print id(inbuffer[0]) == id(1)
True

您可以使用迭代器协议和
itertools.islice
someincomingbytedata
iterable中提取前10个值,然后将其余值放入
inbuffer
。这不会对所有字节使用相同的内存,但它可以尽可能避免使用
bytearray进行不必要的复制:

import itertools

it = iter(someincomingbytedata)
x = bytearray(itertools.islice(it, 10)) # consume the first 10 bytes
inbuffer = bytearray(it)                # consume the rest

如果你真的需要先做你的阅读,然后有效地查看它的各种切片而不复制,你可以考虑使用<代码> NUMPY < /代码>。如果将数据加载到numpy数组中,则以后获取的任何切片都将被视为同一内存中的视图:

import numpy as np

inbuffer = np.array(someincomingdata, dtype=np.uint8)  # load data into an array of bytes
x = inbuffer[:10]  # grab a view of the first ten bytes, which does not require a copy
inbuffer = inbuffer[10:]  # change inbuffer to reference a slice; no copying here either

在您的示例中,
x
将是一个新对象,它保存
inbuffer[0:10]
内容的副本

要在不复制的情况下获取表示,需要使用memoryview(仅在Python 3中可用):

现在
前缀将指向
inbuffer
的前10个字节,
后缀将指向
inbuffer
的剩余内容。这两个对象都保留对
inbuffer
的内部引用,因此不需要显式保留对
inbuffer
inbuffer\u视图的引用

请注意,
前缀
后缀
都是MemoryView,而不是ByteArray或bytes。您可以从中创建字节和字节数组,但此时将复制内容


MemoryView可以传递给任何与实现缓冲区协议的对象一起工作的函数。因此,例如,您可以使用
fh.write(后缀)将它们直接写入文件。

不会为创建的任何新对象返回唯一标识符-Python文档说这不一定指对象引用的内存,除非它是CPython。@VivekMadani无论它是如何实现的。请注意,我使用的
id()
的唯一属性是它在对象上是唯一的(至少在对象同时处于活动状态时)。因此,这两个对象不能在内存中占据相同的位置。我试图实现的是,能够先将所有传入数据保存到缓冲区中,然后在稍后的某个时间点能够截断前N个字节并返回该引用,而无需将截断的字节复制到其他内存位置让我看一下numpy-这看起来像是我想要实现的。我想知道numpy在这里增加了什么价值?我能不能只做:inbuffer=bytearray(someincomingdata)x=inbuffer[:10]#获取前十个字节的视图,这不需要复制inbuffer=inbuffer[10:]#将inbuffer更改为引用一个切片;这里没有复制,我也没有遗漏任何东西吗?从
bytearray
切片和从末尾以外的任何位置删除元素都需要数据的
O(N)
副本
numpy
在对数组进行切片时返回固定数据的“视图”,这正是您所要求的。如果我将inbuffer更改为从偏移量10开始引用切片(上面代码中的最后一条语句),则为了正确理解这一点-当x超出作用域时,前十个字节是否会被GCed?@VivekMadani:我怀疑它们不会被清理,但我不确定numpy的数据共享如何与垃圾收集器配合使用。它可能具有取决于所涉及数据量的启发式方法(例如,10字节:否;10MB:是)。
inbuffer_view = memoryview(inbuffer)
prefix = inbuffer_view[0:10]
suffix = inbuffer_view[10:]