Python: 如何高效地将字节块连接成一个大块?

3
我正在尝试将Amazon S3 python库进行临时修改,以便处理大文件的分块。目前它执行了“self.body = http_response.read()”,所以如果你有一个3G的文件,在获取对其控制之前,你需要将整个文件读入内存中。

我的当前方法是尝试保持库的接口不变,但在读取每个数据块后提供回调函数。类似以下内容:

data = []
while True:
    chunk = http_response.read(CHUNKSIZE)
    if not chunk:
        break
    if callback:
        callback(chunk)
    data.append(chunk)

现在我需要做类似以下的事情:
self.body = ''.join(data)

使用join是将所有块粘合在一起的正确方式吗?还是有其他更好的方法将它们组合起来?

4个回答

3

''join()方法是连接数据块的最佳方法。另一种选择是重复拼接,由于字符串的不可变性和每次拼接都需要创建更多字符串,因此其时间复杂度为O(n**2)。然而,如果使用+=操作符,这种重复拼接在最近版本的CPython中进行了优化,时间复杂度降至O(n),但这种优化只是让它粗略地等同于''.join()方法,后者明确表示其时间复杂度是O(n),其中n表示字节数。


2

您想要解决什么问题?我猜答案取决于您希望如何处理数据。

由于通常不希望将整个3GB文件存储在内存中,因此我不会将块存储在数组中,而是迭代http_response并直接使用适当的文件句柄上的普通write()方法将其写入磁盘中的临时或持久文件中。

如果您确实想要两份数据的副本存储在内存中,则对于您的假设3GB文件,您的方法将需要至少6GB,这对大多数硬件来说可能是重要的。我知道数组连接方法很快,但由于这是一个非常受限制的进程,也许您想找到更好的方法?StringIO(http://docs.python.org/library/stringio.html)创建可以在内存中附加的字符串对象;由于它必须使用不可变字符串工作,因此纯Python版本只是在内部使用了您的数组连接技巧,但基于C的cStringIO实际上可能会在内部附加到内存缓冲区中。我手头没有它的源代码,所以需要检查一下。

如果您确实希望对数据进行某种分析,并且真正希望将其保留在内存中并具有最小的开销,则您可能需要考虑使用Numeric / NumPy中的一些字节数组对象作为StringIO的替代方案。它们是针对大型数组进行优化的高性能代码,可能是您所需的。

作为一个有用的示例,对于具有内存效率迭代器友好方法的通用文件处理对象,您可能需要检查django文件对象块处理代码:http://code.djangoproject.com/browser/django/trunk/django/core/files/base.py


对于上述方法,关于需要6GB而不是3GB的观点非常中肯。我想处理这些块并丢弃它们(在这种情况下只需将它们写入磁盘),但我也希望保留现有的语义以便访问内存中的数据。也许我必须放弃后者。 - Parand

1
在Python3中,bytes对象与str对象是不同的,但我不知道为什么这会有任何问题。

0

join 如果你真的需要把整个字符串拼接起来,那么它看起来很好用,但是你最终还是会把整个字符串存储在 RAM 中。在这种情况下,我会尝试看看是否有一种方法可以处理字符串的每个部分,然后丢弃已处理的部分,这样你只需要一次在内存中保存固定数量的字节。这通常是回调方法的目的所在。(如果你只能一次处理一部分数据块,请使用缓冲区作为队列来存储未处理的数据。)


同意,但我试图保留现有的API,这需要将整个内容保存在内存中。理想情况下,正文应该是一个生成器,而不是一块字节,让用户按照他们的意愿处理它... - Parand

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