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.







