mmap no Python: arquivos na memória

Publicado em: 28/08/2026
Tempo de leitura: 7 minutos
A young girl exploring a library's card catalog, symbolizes research and curiosity.

O módulo mmap cria objetos de memória mapeada que permitem acessar um arquivo como se fosse uma sequência de bytes. Em vez de copiar todo o conteúdo para um objeto Python, o sistema operacional relaciona regiões do arquivo ao espaço de endereçamento do processo e carrega páginas conforme elas são usadas. Isso pode simplificar buscas, edição binária, leitura aleatória, compartilhamento entre processos e processamento de arquivos muito grandes.

Memória mapeada não torna qualquer algoritmo automaticamente mais rápido. O ganho depende do padrão de acesso, tamanho do arquivo, cache do sistema, quantidade de page faults e necessidade de sincronização. O recurso também possui diferenças entre Windows e Unix. Use mmap quando o acesso por posição, a integração com APIs de buffer ou o compartilhamento de páginas realmente trouxerem benefício.

Abra um arquivo para mapeamento

O arquivo precisa ser aberto com modo compatível com o tipo de acesso desejado. Para leitura e escrita, use um descritor que permita ambas.

from pathlib import Path
import mmap

caminho = Path("dados.bin")
with caminho.open("r+b") as arquivo:
    with mmap.mmap(arquivo.fileno(), 0) as mapa:
        print(len(mapa))
        print(mapa[:16])

Um comprimento igual a zero mapeia o tamanho atual do arquivo em plataformas suportadas. Arquivos vazios normalmente não podem ser mapeados dessa forma.

Use context manager

O objeto mmap mantém um recurso nativo aberto. O bloco with garante que o mapeamento seja fechado mesmo quando ocorre uma exceção.

O arquivo original também deve permanecer com lifecycle claro. Em muitos casos, o mapeamento continua válido depois que o descritor é fechado, mas não dependa de detalhes não documentados entre plataformas.

Mapeamento somente leitura

Quando o programa apenas consulta dados, abra o arquivo em modo binário de leitura e solicite acesso de leitura.

with open("indice.bin", "rb") as arquivo:
    with mmap.mmap(
        arquivo.fileno(),
        0,
        access=mmap.ACCESS_READ,
    ) as mapa:
        posicao = mapa.find(b"CHAVE=")
        print(posicao)

Uma tentativa de alterar um mapeamento somente leitura gera erro. Essa restrição reduz o impacto de bugs e comunica melhor a intenção.

Leitura por índice e slicing

O objeto se comporta de forma semelhante a uma sequência mutável de bytes. Um índice retorna um inteiro; um slice retorna bytes.

primeiro = mapa[0]
cabecalho = mapa[0:32]

Slices grandes ainda criam cópias em memória. Para evitar cópia ao integrar com uma API compatível com buffers, use memoryview e controle seu lifecycle.

Posição interna

Além do acesso por índice, mmap oferece métodos semelhantes aos de arquivo, como read(), readline(), seek() e tell().

mapa.seek(100)
bloco = mapa.read(64)
print(mapa.tell())

A posição interna é estado mutável. Duas partes do programa usando o mesmo objeto podem interferir. Prefira offsets explícitos ou sincronização.

Busque padrões

find() e rfind() procuram uma sequência de bytes sem carregar todo o arquivo em uma string separada.

inicio = 0
while True:
    posicao = mapa.find(b"ERROR", inicio)
    if posicao == -1:
        break
    print(posicao)
    inicio = posicao + 5

Esse padrão é útil em logs e formatos binários, mas ainda precisa validar fronteiras e encoding quando o conteúdo representa texto.

Texto e encoding

Um mapeamento trabalha com bytes. Para interpretar texto, decodifique apenas a região necessária.

linha = mapa[inicio:fim].decode("utf-8", errors="strict")

Um slice pode cortar uma sequência multibyte no meio. Encontre delimitadores em bytes ou use um decoder incremental quando processar chunks.

Altere dados no lugar

Um mapeamento gravável permite substituir bytes sem reescrever o arquivo inteiro.

with open("registro.bin", "r+b") as arquivo:
    with mmap.mmap(arquivo.fileno(), 0) as mapa:
        mapa[8:12] = b"DONE"
        mapa.flush()

O tamanho do slice atribuído precisa coincidir com o espaço substituído. mmap não funciona como uma lista que cresce ao receber um slice maior.

Flush e durabilidade

flush() solicita que alterações sejam gravadas no arquivo. O significado exato de durabilidade depende do sistema operacional, filesystem, cache do dispositivo e flags usadas.

Para dados críticos, combine a estratégia com fsync(), escrita transacional, arquivo temporário e rename atômico quando apropriado. Um flush não transforma várias alterações em uma transação.

ACCESS_COPY

ACCESS_COPY cria um mapeamento copy-on-write. O processo vê suas próprias alterações, mas elas não são gravadas no arquivo original.

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

Esse modo é útil para experimentar modificações ou trabalhar com uma visão privada. Ele ainda pode consumir memória conforme páginas são alteradas.

Mapeie apenas uma região

Arquivos enormes podem ser processados em janelas menores. O offset precisa respeitar a granularidade de alocação exigida pela plataforma.

gran = mmap.ALLOCATIONGRANULARITY
offset = (inicio // gran) * gran
deslocamento = inicio - offset
comprimento = deslocamento + tamanho

with mmap.mmap(
    arquivo.fileno(),
    comprimento,
    access=mmap.ACCESS_READ,
    offset=offset,
) as mapa:
    dados = mapa[deslocamento:deslocamento + tamanho]

Alinhar o offset é uma das fontes mais comuns de erro em mapeamentos parciais.

Arquivos maiores que a memória RAM

O sistema pode mapear um arquivo maior que a RAM porque páginas são carregadas sob demanda. Entretanto, percorrer o arquivo de forma aleatória pode gerar muitos page faults e thrashing.

Organize o acesso de maneira sequencial quando possível, meça resident set e evite manter vários mapeamentos gigantes sem necessidade.

Redimensionamento

resize() pode alterar o tamanho do mapeamento em determinadas plataformas e modos. O suporte e as restrições variam.

Para um fluxo portátil, redimensione o arquivo antes de mapear ou feche o mapeamento, altere o arquivo e crie um novo mapa. Nunca continue usando offsets calculados para o tamanho antigo.

Crie um arquivo com tamanho definido

Um arquivo precisa ter bytes suficientes antes de receber escrita em regiões futuras.

tamanho = 1024 * 1024
with open("bloco.bin", "w+b") as arquivo:
    arquivo.truncate(tamanho)
    with mmap.mmap(arquivo.fileno(), tamanho) as mapa:
        mapa[0:4] = b"DATA"

Arquivos esparsos e alocação física dependem do filesystem. Não presuma que truncate() reservou todo o espaço no dispositivo.

Mapeamento anônimo

Em várias plataformas, um mapeamento pode ser criado sem arquivo para funcionar como memória compartilhada ou região de trabalho.

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

O mecanismo de compartilhamento e nomeação difere entre Windows e Unix. Para processos Python, avalie também multiprocessing.shared_memory.

Compartilhamento entre processos

Dois processos que mapeiam a mesma região de arquivo podem observar alterações compartilhadas, dependendo do modo. Isso não oferece sincronização automática.

Use locks, semáforos, protocolo de versão, checksums e uma ordem clara de publicação. Um leitor não deve interpretar uma estrutura enquanto o escritor a modifica parcialmente.

Concorrência e threads

O objeto expõe estado mutável e posição interna. Várias threads podem ler regiões independentes, mas operações que usam seek() e read() precisam de coordenação.

Para escrita concorrente, proteja regiões ou centralize as alterações. A ausência de exceção não garante consistência lógica.

Arquivos alterados por outro processo

Se outro processo truncar um arquivo enquanto ele está mapeado, o comportamento pode incluir erros graves, sinais do sistema ou acesso inválido.

Defina ownership do arquivo e evite redimensionamento enquanto existirem leitores. Use geração de arquivo novo e troca atômica para atualizações completas.

Buffer protocol e memoryview

Um memoryview permite que bibliotecas processem o mapeamento sem uma cópia adicional.

with mmap.mmap(arquivo.fileno(), 0, access=mmap.ACCESS_READ) as mapa:
    view = memoryview(mapa)
    try:
        consumir_buffer(view[100:200])
    finally:
        view.release()

O mapeamento não pode ser fechado enquanto existirem views exportadas. Libere-as explicitamente.

Integração com struct

Formatos binários com campos fixos podem ser lidos diretamente com struct.unpack_from().

import struct

versao, tamanho = struct.unpack_from("!HI", mapa, 0)

Valide o tamanho mínimo antes de desempacotar e não confie em campos provenientes de arquivos externos.

Performance

Compare mmap com leitura em chunks, readinto() e APIs de alto nível. Em acesso sequencial simples, um loop de leitura com buffer pode ser igualmente rápido e mais portátil.

Meça tempo total, page faults, memória residente e comportamento sob carga real. Benchmarks com arquivos já presentes no cache podem produzir conclusões enganosas.

Segurança

Um arquivo mapeado continua sendo entrada. Valide assinatura, magic bytes, tamanho, offsets, contagens e limites antes de acessar posições calculadas.

Não use valores internos do arquivo para formar slices ou multiplicações sem verificar overflow lógico e limites. Um arquivo malicioso pode induzir consumo excessivo ou leitura de regiões inválidas.

Tratamento de erros

Abertura, mapeamento, flush e acesso podem gerar OSError, ValueError, TypeError ou exceções de índice.

try:
    with open(caminho, "rb") as arquivo:
        with mmap.mmap(arquivo.fileno(), 0, access=mmap.ACCESS_READ) as mapa:
            analisar(mapa)
except (OSError, ValueError) as erro:
    raise RuntimeError(f"falha ao mapear {caminho}") from erro

Inclua caminho e operação no diagnóstico sem expor dados sensíveis.

Testes

Teste arquivo vazio, tamanho mínimo, arquivo truncado, permissões negadas, offset desalinhado, alterações concorrentes, dados inválidos, slices nos limites, flush, Windows e Unix.

Use diretórios temporários e arquivos reais. Um mock de arquivo não reproduz page faults, alinhamento e semântica do sistema operacional.

Erros comuns

Os erros mais frequentes são mapear arquivo vazio, usar modo incompatível, esquecer alinhamento do offset, presumir que slices não copiam, fechar o mapa com memoryview ativo, modificar tamanho externamente, achar que flush() cria transação e usar acesso aleatório sem medir page faults.

Conclusão

mmap permite tratar arquivos e regiões compartilhadas como buffers endereçáveis. Ele é especialmente útil para busca em arquivos grandes, formatos binários, acesso aleatório e integração com APIs de buffer.

Use lifecycle explícito, valide limites, sincronize escritores e compare com leitura em chunks antes de escolher. Consulte a documentação oficial de mmap, o artigo de multiprocessing no Python e o guia de contextlib no Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Close-up of a hand pointing at audio editing software on a monitor in a recording studio.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    importlib.metadata: versões e plugins

    Aprenda importlib.metadata no Python para consultar versões, requisitos, arquivos, distribuições, entry points e plugins sem importar pacotes.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Neatly arranged binders and magazines on library shelves showcasing organization.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources: leia arquivos de pacotes

    Aprenda importlib.resources no Python para ler templates, dados e arquivos de pacotes com Traversable, files e as_file em wheels e

    Ler mais

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

    runpy no Python: execute módulos e scripts

    Aprenda runpy no Python para executar módulos e scripts, controlar __main__, run_path, alter_sys, namespaces, testes e isolamento.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil no Python: descubra pacotes

    Aprenda pkgutil no Python para listar módulos, percorrer pacotes, descobrir plugins, consultar importers e ler recursos com segurança.

    Ler mais

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

    modulefinder no Python: descubra imports

    Aprenda modulefinder no Python para descobrir imports, dependências transitivas, módulos ausentes, paths, plugins e limitações da análise estática.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Vivid close-up of code on a computer screen showcasing programming details.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    py_compile no Python: compile um arquivo

    Aprenda py_compile no Python para compilar um arquivo, controlar .pyc, dfile, otimização, invalidação por hash e erros de build.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026