【发布时间】:2020-06-23 10:51:14
【问题描述】:
假设我有非常大的字节对象(加载二进制文件后),我想逐个读取部分并推进起始位置直到它到达终点。我使用切片来完成此操作。我担心每次我请求切片时python都会创建全新的副本,而不是简单地给我指向我想要的位置的内存地址。
简单示例:
data = Path("binary-file.dat").read_bytes()
total_length = len(data)
start_pos = 0
while start_pos < total_length:
bytes_processed = decode_bytes(data[start_pos:]) # <---- ***
start_pos += bytes_processed
在上面的示例中,由于切片,python 是否从 start_pos 开始创建了字节对象的全新副本。如果是这样,那么避免数据复制并仅使用指针传递到字节数组的相关位置的最佳方法是什么。
【问题讨论】:
-
切片创建新的
bytes对象。现在,python可能不复制底层缓冲区,而是在切片之间共享一个缓冲区。但是,一般情况下是这样,它会有效地复制底层缓冲区。自己尝试一下,b = b'a'*1_000_000_000应该会占用大量内存。现在有趣的是,如果您进行完整复制,它似乎不会复制底层缓冲区,所以b2 = b[:],然而,其他任何东西,它都会这样做,所以b3 = b[1:]。 -
如果您正在使用
bytes并且想要一种内存高效的方式来对它们进行切片,请使用memoryview注意,python 没有指针 -
@juanpa.arrivillaga,谢谢我来自其他编程语言,我认为 pythin 将 byte 视为原始类型,因此可能是副本(如果它是具有不同对象类型的列表,它仍然会复制对新列表的引用)所以这在我的情况下并不理想,因为我必须每次遍历 12-20MB 的数据 4000+ 字节,并且有这样的文件流,正如你所说的 memoryview 可能是解决方案。谢谢
-
@jpnadas 是的,这提供了一些见解,但我说的是字节对象,因为它是不可变的(您无法修改数组的任何元素)我认为切片不会创建新的或复制引用。
-
@Tekz 关于列表的其他问题实际上在这里完全无关紧要。
list对象包含其他 python 对象。bytes对象本质上是对原始字节缓冲区的面向对象的包装器。尽管它充当容器,但它实际上并不 包含 其他 python 对象,尽管索引返回 python 对象(实际上是整数),您可以使用其他bytes对象进行成员资格测试,但在内部,没有对其他python对象的引用,只是一个原始的字节缓冲区,基本上是一个char数组。