Шаг 88.
Python: сборник рецептов.
Файлы и ввод-вывод. Отображаемые в память бинарные файлы

    На этом шаге мы рассмотрим использование модуля mmap.

Задача

    Вы хотите отобразить в память бинарный файл в форме изменяемого массива байтов - вероятно, для произвольного доступа к его содержимому или изменений прямо на месте.

Решение

    Используйте модуль mmap для отображения файлов в память. Вот полезная функция, с помощью которой можно открыть файл и отобразить его в память переносимым способом:

>>> import os
>>> import mmap
>>> def memory_map(filename, access=mmap.ACCESS_WRITE):
	size = os.path.getsize(filename)
	fd = os.open(filename, os.O_RDWR)
	return mmap.mmap(fd, size, access=access)

>>> size = 1000000
>>> with open('data', 'wb') as f:
	f.seek(size-1)
	f.write(b'\x00')

	
999999
1
>>> 

    А вот пример отображения содержимого в память с помощью функции memory_ map():

>>> m = memory_map('data')
>>> len(m)
1000000
>>> m[0:10]
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> m[0]
0
>>> # Переприсваивание среза
>>> m[0:11] = b'Hello World'
>>> m.close()
>>> # Проверка того, что изменения были сделаны
>>> with open('data', 'rb') as f:
	print(f.read(11))

	
b'Hello World'
>>> 

    Объект mmap, возвращаемый функцией mmap(), может быть также использован в качестве менеджера контекста. В этом случае отображенный файл закрывается автоматически. Например:

>>> with memory_map('data') as m:
	print(len(m))
	print(m[0:10])

	
1000000
b'Hello Worl'
>>> m.closed
True
>>> 

    По умолчанию показанная функция memory_map() открывает файл и на чтение, и на запись. Любые изменения данных копируются в исходный файл. Если требуется организовать доступ только для чтения, предоставьте mmap.ACCESSREAD в качестве аргумента access. Например:

m = memory_map(filename, mmap.ACCESS_READ)

    Если вы намерены локально изменять данные, но не хотите, чтобы изменения записывались в исходный файл, используйте mmap.ACCESSCOPY:

m = memory_map(filename, mmap.ACCESS_COPY)


Обсуждение

    Использование mmap для отображения файлов в память может стать элегантным и эффективным решением для произвольного доступа к содержимому файла. Например, вместо открытия файла и выполнения различных комбинаций вызовов seek(), read() и write() вы просто отображаете файл и получаете доступ к любым данным через операции извлечения срезов.

    Обычно память, выделяемая mmap(), выглядит как объект bytearray. Однако вы можете интерпретировать данные по-разному, используя функцию memoryview(). Например:

>>> m = memory_map('data')
>>> # Представление памяти беззнаковых целых чисел
>>> v = memoryview(m).cast('I')
>>> v[0] = 7
>>> m[0:4]
b'\x07\x00\x00\x00'
>>> m[0:4] = b'\x07\x01\x00\x00'
>>> v[0]
263
>>> 

    Стоит отметить, что отображение файла в память не вызывает чтения файла в память целиком. Он не копируется в некий буфер памяти или массив. Вместо этого операционная система выделяет участок виртуальной памяти под содержимое файла. По мере того как вы обращаетесь к различным участкам, эти куски файла будут читаться и отображаться в участок памяти по мере необходимости. Однако части файла, к которым никогда не производился доступ, останутся на диске.

    Если не единственный интерпретатор Python отображает в память один и тот же файл, получившийся объект mmap может быть использован для обмена данными между интерпретаторами. Интерпретаторы могут читать и записывать данные одновременно, и изменения, которые были сделаны в одном интерпретаторе, автоматически будут доступны в других. Очевидно, что синхронизация требует дополнительного внимания, но этот подход иногда используется в качестве альтернативы передаче данных через каналы или сокеты.

    Показанный выше рецепт написан максимально обобщенно, он работает и в Windows, и в Unix. Однако стоит отметить, что есть специфические для каждой платформы отличия в том, как "под капотом" работает mmap(). Также есть возможности по созданию анонимно отображенных участков памяти. Если вас это интересует, прочтите соответствующий раздел документации Python.


http://docs.python.org/3/library/mmap.html.

    На следующем шаге мы рассмотрим манипулирование путями к файлам.




Предыдущий шаг Содержание Следующий шаг