Mmap.mmap: примеры (PYTHON)
mmap.mmap(fileno: int, length: int, **kwargs): mmap objectФункция mmap.mmap в Python
Функция mmap.mmap() создает объект отображения памяти, который позволяет работать с файлами или ресурсами, как с большими байтовыми массивами в оперативной памяти. Она предоставляет интерфейс для чтения и записи данных, используя механизмы виртуальной памяти операционной системы, что часто ускоряет доступ к большим файлам.
Использование этого метода целесообразно при обработке объемных файлов, когда полная загрузка в память нежелательна или невозможна. Это также полезно для разделяемой памяти между процессами.
Сигнатура функции выглядит так:
mmap.mmap(fileno, length, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ|mmap.PROT_WRITE, access=mmap.ACCESS_DEFAULT, offset=0)
Аргументы:
- fileno (целое число): Целочисленный файловый дескриптор, полученный методом
fileno()файлового объекта, либо константа-1для создания анонимного отображения. - length (целое число): Размер отображаемой области в байтах. Для файлов часто используют
0, чтобы отобразить весь файл. - flags (целое число, опционально): Указывает тип отображения. По умолчанию
mmap.MAP_SHARED(изменения видны другим процессам). Альтернатива -mmap.MAP_PRIVATE(копия при записи). - prot (целое число, опционально): Задает защиту памяти. По умолчанию
mmap.PROT_READ | mmap.PROT_WRITE(чтение и запись). Возможны комбинацииmmap.PROT_READ,mmap.PROT_WRITE,mmap.PROT_EXEC. - access (целое число, опционально): Альтернативный способ задания доступа. Используется вместо
protиflags. Варианты:mmap.ACCESS_READ(только чтение),mmap.ACCESS_WRITE(чтение и запись, сквозная запись),mmap.ACCESS_COPY(копия при записи). - offset (целое число, опционально): Смещение от начала файла, с которого начинается отображение. Должно быть кратно размеру страницы памяти (обычно 4096).
Возвращаемое значение:
Функция возвращает объект mmap.mmap, который поддерживает интерфейс, схожий с объектом bytes или bytearray. С ним можно работать как с изменяемой последовательностью байтов, используя срезы, методы поиска и замены.
Короткие примеры использования
Пример 1: Чтение файла с доступом только для чтения.
import mmap
with open('example.txt', 'r+b') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
# Чтение первых 10 байт
data = mm[:10]
print(data)
b'First line'
Пример 2: Запись в файл с использованием отображения.
import mmap
with open('example.txt', 'r+b') as f:
with mmap.mmap(f.fileno(), 0) as mm:
# Замена слова в файле
if mm.find(b'old') != -1:
mm.seek(mm.find(b'old'))
mm.write(b'new')
Пример 3: Анонимное отображение для обмена данными между процессами.
import mmap, os
# Создание анонимной области памяти размером 100 байт
mm = mmap.mmap(-1, 100)
mm.write(b'Data for other process')
mm.seek(0)
print(mm.read(30))
b'Data for other process'
Похожие функции в Python
В Python существуют другие методы для работы с данными, которые могут служить альтернативой в определенных сценариях.
- open().read() / write(): Стандартное чтение и запись файлов. Предпочтительнее для небольших файлов или последовательного доступа, когда не требуется случайный доступ к большим блокам данных. Не создает нагрузку на управление памятью ОС.
- bytearray / memoryview: Встроенные типы для работы с бинарными данными в памяти. Используются, когда данные уже загружены в оперативную память и требуют изменений.
memoryviewпозволяет создавать представления без копирования данных. - numpy.memmap: Функция из библиотеки NumPy для отображения файлов в память как многомерных массивов. Является предпочтительным выбором для числовых данных и научных вычислений, так как предоставляет удобный интерфейс для работы с массивами.
- multiprocessing.Array / Value: Примитивы разделяемой памяти для межпроцессного взаимодействия. Более высокоуровневые, чем анонимное отображение
mmap, и часто проще в использовании для конкретных типов данных.
Альтернативы в других языках программирования
Концепция отображения файлов в память присутствует во многих языках, но реализация и API различаются.
C# (.NET): Класс MemoryMappedFile из пространства имен System.IO.MemoryMappedFiles.
using System.IO.MemoryMappedFiles;
using System.IO;
// Создание отображенного файла
using (var mmf = MemoryMappedFile.CreateFromFile("data.dat"))
using (var accessor = mmf.CreateViewAccessor())
{
byte value = accessor.ReadByte(0);
accessor.Write(0, (byte)42);
}
Java: Класс java.nio.channels.FileChannel с методом map().
import java.nio.*;
import java.nio.channels.FileChannel;
import java.io.RandomAccessFile;
RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel channel = file.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());
buffer.put(0, (byte) 'A');
byte b = buffer.get(0);
Golang: Пакет syscall или golang.org/x/exp/mmap. В Go подход более низкоуровневый.
import "golang.org/x/exp/mmap"
at, _ := mmap.Open("data.bin")
defer at.Close()
data := make([]byte, 10)
at.ReadAt(data, 0) // Чтение по смещению
JavaScript/Node.js: Нет прямой поддержки в стандартной библиотеке. Для работы с большими файлами используют потоки (Streams API) или модули типа fs.open с чтением в буфер.
PHP: Функция mmap() доступна через модуль ext/mmap (нестандартный). Чаще используют функции вроде fopen и fread.
Ключевое отличие Python-реализации - удобный объектно-ориентированный интерфейс, интегрированный с контекстными менеджерами (with), что обеспечивает автоматическое закрытие.
Типичные ошибки при использовании
1. Некорректный файловый дескриптор. Передача неверного или закрытого дескриптора.
import mmap
f = open('test.txt', 'r')
f.close()
try:
mm = mmap.mmap(f.fileno(), 0)
except ValueError as e:
print(f'Ошибка: {e}')
Ошибка: cannot mmap an empty file
2. Ошибка размера (length=0 для пустого файла). Попытка отобразить файл нулевой длины без указания размера для анонимного отображения.
import mmap
open('empty.txt', 'w').close() # Создаем пустой файл
with open('empty.txt', 'r+b') as f:
try:
mm = mmap.mmap(f.fileno(), 0)
except ValueError as e:
print(f'Ошибка: {e}')
Ошибка: cannot mmap an empty file
3. Попытка записи при открытии с доступом только для чтения (ACCESS_READ).
import mmap
with open('test.txt', 'w+b') as f:
f.write(b'sample')
f.flush()
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
try:
mm[0] = 65
except TypeError as e:
print(f'Ошибка: {e}')
Ошибка: mmap can't modify a readonly memory map.
4. Неверное смещение (offset), не кратное размеру страницы.
import mmap
with open('test.txt', 'w+b') as f:
f.write(b'x' * 5000)
try:
# Смещение не кратно размеру страницы (например, 1000)
mm = mmap.mmap(f.fileno(), 100, offset=1000)
except ValueError as e:
print(f'Ошибка: {e}')
Ошибка: mmap offset must be a multiple of the page size (4096)
Изменения в последних версиях Python
В Python 3.8 были добавлены новые методы для объектов mmap: madvise() и shrink().
madvise()позволяет давать подсказки ядру ОС о предполагаемом шаблоне доступа к памяти (например,mmap.MADV_RANDOMдля случайного доступа), что может оптимизировать производительность.shrink()пытается уменьшить размер отображения, освобождая неиспользуемую память.
В Python 3.10 появилась возможность использовать константу mmap.MAP_POPULATE (Linux) в аргументе flags для предварительной загрузки страниц в память, что может уменьшить количество page faults при последующем доступе.
Также в более ранних версиях были улучшены сообщения об ошибках и документация.
Расширенные примеры использования
Пример 1: Многопроцессное взаимодействие через анонимную память. Отображение памяти может служить разделяемой памятью для процессов.
import mmap, os
from multiprocessing import Process
def writer():
with mmap.mmap(-1, 100, tagname='shared_mem') as mm:
mm.write(b'Hello from writer!')
# Ждем, пока читатель прочитает
import time
time.sleep(2)
def reader():
import time
time.sleep(1) # Ждем, пока запишется
# Открываем существующее отображение по имени
with mmap.mmap(-1, 100, tagname='shared_mem') as mm:
mm.seek(0)
data = mm.read(50)
print(f'Reader получил: {data}')
if __name__ == '__main__':
p1 = Process(target=writer)
p2 = Process(target=reader)
p1.start()
p2.start()
p1.join()
p2.join()
Reader получил: b'Hello from writer!'
Пример 2: Работа со структурированными данными с использованием модуля struct.
import mmap, struct
with open('data.bin', 'w+b') as f:
# Записываем 3 целых числа
f.write(struct.pack('iii', 10, 20, 30))
f.flush()
with mmap.mmap(f.fileno(), 0) as mm:
# Читаем второе число (смещение 4 байта, т.к. int обычно 4 байта)
second_number = struct.unpack('i', mm[4:8])[0]
print(f'Второе число: {second_number}')
# Меняем третье число
mm[8:12] = struct.pack('i', 99)
Второе число: 20
Пример 3: Поиск и замена всех вхождений подстроки в большом файле.
import mmap, os
def replace_in_file(filename, old, new):
if len(old) != len(new):
raise ValueError('Длины строк должны совпадать')
with open(filename, 'r+b') as f:
with mmap.mmap(f.fileno(), 0) as mm:
pos = 0
while True:
pos = mm.find(old.encode(), pos)
if pos == -1:
break
mm.seek(pos)
mm.write(new.encode())
pos += len(old)
# Тестирование
with open('big.txt', 'w') as f:
f.write('cat dog cat bird cat\n' * 10000)
replace_in_file('big.txt', 'cat', 'CAT')
# Проверка первых 100 байт
with open('big.txt', 'rb') as f:
print(f.read(100))
b'CAT dog CAT bird CAT\nCAT dog CAT bird CAT\nCAT dog CAT bird CAT\nCAT dog CAT bird CAT\nCAT do'
Пример 4: Использование метода madvise() для оптимизации (Python 3.8+).
import mmap
with open('huge.bin', 'r+b') as f:
with mmap.mmap(f.fileno(), 0) as mm:
# Сообщаем ядру, что доступ будет последовательным
mm.madvise(mmap.MADV_SEQUENTIAL)
# Читаем данные последовательно
data = mm.read(1024*1024) # 1 МБ
# После завершения чтения, можно освободить память
mm.madvise(mmap.MADV_DONTNEED)