Python mmap: Memory-Mapped Files

Published on: August 28, 2026
Reading time: 6 minutes
Composition of black and white photos and old travel log placed with vintage island map and obsolete photo camera on narrow wooden table

The mmap module creates memory-mapped objects that let a program access file content as an addressable byte sequence. Instead of copying an entire file into a Python object, the operating system maps regions of the file into the process address space and loads pages as they are touched. This can simplify random access, binary editing, searching, inter-process sharing, and work with very large files.

Memory mapping does not make every algorithm faster. Results depend on access patterns, page faults, operating-system cache behavior, file size, and synchronization needs. The API also differs in selected details between Windows and Unix. Use mmap when position-based access, buffer-protocol integration, or shared pages provide a real advantage.

Open a file for mapping

The file mode must match the desired access. Use a descriptor opened for both reading and writing when the mapping will be modified.

from pathlib import Path
import mmap

path = Path("data.bin")
with path.open("r+b") as file:
    with mmap.mmap(file.fileno(), 0) as mapping:
        print(len(mapping))
        print(mapping[:16])

A length of zero maps the current file size on supported platforms. Empty files normally cannot be mapped this way.

Use context managers

An mmap object owns a native resource. A with block guarantees closure even when parsing or processing fails.

Keep the original file lifecycle explicit as well. Although a mapping may remain usable after the descriptor closes on many systems, avoid relying on undocumented cross-platform behavior.

Read-only mappings

When an application only inspects data, open the file in binary read mode and request read access.

with open("index.bin", "rb") as file:
    with mmap.mmap(
        file.fileno(),
        0,
        access=mmap.ACCESS_READ,
    ) as mapping:
        position = mapping.find(b"KEY=")
        print(position)

Attempts to modify a read-only mapping fail. This restriction reduces the impact of bugs and clearly documents intent.

Indexes and slices

The object behaves much like a mutable byte sequence. One index returns an integer, while a slice returns a bytes object.

first = mapping[0]
header = mapping[0:32]

Large slices still copy data. Use memoryview for zero-copy integration with compatible APIs, and manage its lifetime carefully.

The internal cursor

Besides indexed access, mmap provides file-like methods including read(), readline(), seek(), and tell().

mapping.seek(100)
block = mapping.read(64)
print(mapping.tell())

The cursor is mutable shared state. Two parts of a program using the same object can interfere. Prefer explicit offsets or synchronization.

Search byte patterns

find() and rfind() search for byte sequences without first creating a second in-memory copy of the full file.

start = 0
while True:
    position = mapping.find(b"ERROR", start)
    if position == -1:
        break
    print(position)
    start = position + 5

This works well for logs and binary formats, but text content still requires correct encoding and boundary handling.

Text and encoding

A mapping contains bytes. Decode only the required region.

line = mapping[start:end].decode("utf-8", errors="strict")

A slice may cut through a multibyte character. Find delimiters at the byte level or use an incremental decoder when processing chunks.

Modify bytes in place

A writable mapping can replace bytes without rewriting the entire file.

with open("record.bin", "r+b") as file:
    with mmap.mmap(file.fileno(), 0) as mapping:
        mapping[8:12] = b"DONE"
        mapping.flush()

The assigned byte count must match the replaced region. An mmap object does not grow like a list when a longer slice is assigned.

Flush and durability

flush() requests that changed pages be written. Exact durability depends on the operating system, filesystem, device cache, and flags.

For critical data, combine mapping with transactional design, temporary files, atomic rename, and fsync() where appropriate. A flush does not turn several writes into one atomic transaction.

Private copy-on-write mappings

ACCESS_COPY creates a private copy-on-write view. The process observes its own changes, but the original file is not updated.

with mmap.mmap(
    file.fileno(),
    0,
    access=mmap.ACCESS_COPY,
) as mapping:
    mapping[0:4] = b"TEST"

This is useful for experimental transformations or temporary views. Modified pages may still increase process memory.

Map only one region

Very large files can be processed through smaller windows. The offset must respect the allocation granularity required by the platform.

granularity = mmap.ALLOCATIONGRANULARITY
offset = (start // granularity) * granularity
delta = start - offset
length = delta + size

with mmap.mmap(
    file.fileno(),
    length,
    access=mmap.ACCESS_READ,
    offset=offset,
) as mapping:
    data = mapping[delta:delta + size]

Misaligned offsets are a common source of failures in partial mappings.

Files larger than RAM

An operating system can map a file larger than physical memory because pages are loaded on demand. However, random traversal may trigger heavy paging and thrashing.

Prefer sequential access when possible, measure resident memory and page faults, and avoid keeping many huge mappings open without a reason.

Resize carefully

resize() can change a mapping under selected platform and access conditions. Support and restrictions vary.

For portable code, close the mapping, resize the file, and create a new mapping. Recalculate every offset derived from the previous size.

Create a pre-sized file

The underlying file must contain enough bytes before writing to future regions.

size = 1024 * 1024
with open("block.bin", "w+b") as file:
    file.truncate(size)
    with mmap.mmap(file.fileno(), size) as mapping:
        mapping[0:4] = b"DATA"

Sparse-file behavior and physical allocation depend on the filesystem. truncate() does not necessarily reserve every device block.

Anonymous mappings

On supported platforms, a mapping can be created without a file and used as a working buffer or shared-memory region.

with mmap.mmap(-1, 4096) as mapping:
    mapping[:5] = b"hello"
    print(mapping[:5])

Naming and sharing rules differ between Windows and Unix. Python processes may also benefit from multiprocessing.shared_memory.

Share data between processes

Processes mapping the same file region can observe shared updates, depending on access mode. Mapping does not provide synchronization.

Use locks, semaphores, version markers, checksums, and a publication protocol. A reader must not parse a structure while a writer is changing half of it.

Threads and shared state

Independent indexed reads can be simple, but cursor-based operations such as seek() and read() require coordination when an object is shared.

Protect concurrent writes or centralize modification. The absence of an exception does not imply logical consistency.

External file changes

If another process truncates a mapped file, later access may produce severe errors, operating-system signals, or invalid memory references.

Define ownership and prohibit resizing while readers exist. For complete updates, create a new file and switch it into place atomically.

Buffer protocol and memoryview

A memoryview lets compatible libraries consume mapping bytes without another copy.

with mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as mapping:
    view = memoryview(mapping)
    try:
        consume_buffer(view[100:200])
    finally:
        view.release()

The mapping cannot close while exported views remain active. Release them explicitly.

Integrate with struct

Fixed-layout binary formats can be decoded directly using struct.unpack_from().

import struct

version, length = struct.unpack_from("!HI", mapping, 0)

Check the minimum buffer size first, and never trust lengths read from external files.

Measure performance

Compare mmap with buffered chunk reading, readinto(), and higher-level APIs. For simple sequential scans, buffered reads may be equally fast and easier to maintain.

Measure total time, resident memory, page faults, and behavior under realistic concurrency. Benchmarks against files already in the page cache can be misleading.

Security

A mapped file is still untrusted input. Validate magic bytes, signatures, sizes, counts, offsets, and boundaries before indexing calculated positions.

Do not use file-provided values in slice arithmetic without limits. A malicious file can trigger excessive memory use or invalid access.

Error handling

Opening, mapping, flushing, and indexing can raise OSError, ValueError, TypeError, or index-related exceptions.

try:
    with open(path, "rb") as file:
        with mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as mapping:
            analyze(mapping)
except (OSError, ValueError) as error:
    raise RuntimeError(f"could not map {path}") from error

Include the path and operation in diagnostics without exposing sensitive content.

Testing

Test empty files, minimum size, truncation, denied permissions, misaligned offsets, concurrent changes, invalid binary structures, boundary slices, flush behavior, Windows, and Unix.

Use real temporary files. File mocks do not reproduce alignment, page faults, or operating-system semantics.

Common mistakes

Common failures include mapping empty files, using an incompatible open mode, forgetting offset alignment, assuming slices are zero-copy, closing with an active memoryview, resizing externally, treating flush() as a transaction, and choosing random access without measuring paging cost.

Conclusion

mmap turns files and shared regions into addressable buffers. It is especially useful for large-file searches, binary formats, random access, and buffer-protocol integration.

Keep ownership explicit, validate boundaries, synchronize writers, and benchmark against buffered I/O. Consult the official mmap documentation, Python multiprocessing, and Python contextlib.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Detailed view of programming code in a dark theme on a computer screen.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python struct: Binary Data Formats

    Learn Python struct for binary packing, endianness, offsets, padding, buffers, sockets, versioned layouts, and safe validation.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Close-up of a USB pen drive being inserted into a laptop USB port on a white surface.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python importlib.metadata: Versions and Plugins

    Learn Python importlib.metadata to query versions, requirements, files, distributions, entry points, and plugins without importing packages.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources: Read Packaged Files

    Learn Python importlib.resources to read templates and package data with Traversable, files, and as_file across wheels, ZIPs, and frozen apps.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python runpy: Execute Modules and Scripts

    Learn Python runpy to execute modules and scripts, control __main__, run_path, alter_sys, namespaces, testing, and process isolation.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pkgutil: Discover Packages

    Learn Python pkgutil to list modules, walk packages, discover plugins, inspect importers, and read package resources safely.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Colorful stacked shipping containers at Hamburg port, showcasing global trade and logistics.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python modulefinder: Discover Imports

    Learn Python modulefinder to discover imports, transitive dependencies, missing modules, paths, plugins, and limitations of static analysis.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026