Python: эффективно объединить куски байтов в один большой кусок?

Я пытаюсь приспособить библиотеку Python для Amazon S3, чтобы можно было обрабатывать большие файлы по частям. Прямо сейчас он выполняет "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)

Является ли присоединение правильным способом сделать это или есть другой (лучший) способ собрать все части вместе?

Ответов (4)

Решение

хм - какую проблему вы пытаетесь решить? Я подозреваю, что ответ зависит от того, что вы пытаетесь сделать с данными.

Поскольку, как правило, вам не нужен весь файл 3 Гб в памяти, я бы не сохранял фрагменты в массиве, а перебирал http_response и записывал его прямо на диск во временном или постоянном файле с помощью обычного write () на соответствующий дескриптор файла.

если вам нужны две копии данных в памяти, ваш метод потребует не менее 6 ГБ для вашего гипотетического файла 3 ГБ, что, по-видимому, важно для большинства оборудования. Я знаю, что методы объединения массивов бывают быстрыми и все такое, но, поскольку это действительно ограниченный процесс, может быть, вы хотите найти способ сделать это лучше? StringIO ( http://docs.python.org/library/stringio.html ) создает строковые объекты, которые можно добавлять в память; чистый python, поскольку он должен работать с неизменяемыми строками, просто использует ваш трюк с объединением массивов внутри, но cStringIO на основе c может фактически добавляться во внутренний буфер памяти. У меня под рукой нет его исходного кода, так что его нужно проверить.

если вы действительно хотите провести какой-то анализ данных и действительно хотите сохранить их в памяти с минимальными накладными расходами, вы можете рассмотреть некоторые объекты байтовых массивов из Numeric / NumPy в качестве альтернативы StringIO. они представляют собой высокопроизводительный код, оптимизированный для больших массивов и, возможно, именно то, что вам нужно.

В качестве полезного примера для объекта обработки файлов общего назначения, который имеет подход, ориентированный на эффективную память и дружественный к итераторам, вы можете проверить код обработки фрагментов объекта файла django: http://code.djangoproject.com/browser/django /trunk/django/core/files/base.py .

В python3 bytes объекты отличаются от str, но я не знаю причин, почему с этим что-то не так.

'' join () - лучший метод для объединения фрагментов данных. Альтернатива сводится к повторной конкатенации, которая составляет O (n ** 2) из-за неизменности строк и необходимости создавать больше при каждой конкатенации. Учитывая, что эта повторяющаяся конкатенация оптимизирована последними версиями CPython, если используется с + =, чтобы стать O (n), но эта оптимизация в любом случае дает ему только приблизительный эквивалент '' .join (), что явно превышает O (n). количество байтов.

join кажется прекрасным, если вам действительно нужно собрать всю строку вместе, но тогда вы все равно просто сохраните все это в ОЗУ. В подобной ситуации я бы попытался увидеть, есть ли способ обработать каждую часть строки, а затем отбросить обработанную часть, поэтому вам нужно только удерживать фиксированное количество байтов в памяти за раз. Обычно в этом заключается суть подхода обратного вызова. (Если вы можете обрабатывать только часть фрагмента за раз, используйте буфер в качестве очереди для хранения необработанных данных.)