mmap en Python: archivos en memoria

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

El módulo mmap crea objetos de memoria mapeada que permiten acceder al contenido de un archivo como una secuencia direccionable de bytes. En lugar de copiar todo el archivo a un objeto Python, el sistema operativo relaciona regiones del archivo con el espacio de direcciones del proceso y carga páginas a medida que se utilizan. Esto puede simplificar acceso aleatorio, búsqueda, edición binaria, intercambio entre procesos y procesamiento de archivos muy grandes.

La memoria mapeada no vuelve rápido cualquier algoritmo. El resultado depende del patrón de acceso, page faults, cache del sistema, tamaño del archivo y necesidad de sincronización. También existen diferencias entre Windows y Unix. Usa mmap cuando el acceso por posición, la integración con el buffer protocol o el uso compartido de páginas ofrezcan una ventaja real.

Abre un archivo para mapearlo

El modo del archivo debe ser compatible con el tipo de acceso. Para lectura y escritura, usa un descriptor que permita ambas operaciones.

from pathlib import Path
import mmap

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

Una longitud igual a cero mapea el tamaño actual del archivo en plataformas compatibles. Los archivos vacíos normalmente no pueden mapearse de esta forma.

Usa context managers

Un objeto mmap mantiene un recurso nativo. El bloque with garantiza el cierre incluso cuando el procesamiento falla.

Mantén también explícito el lifecycle del archivo original. Aunque un mapa puede seguir válido después de cerrar el descriptor en muchos sistemas, evita depender de detalles no documentados.

Mapeo de solo lectura

Si la aplicación únicamente consulta datos, abre el archivo en modo binario de lectura y solicita acceso de lectura.

with open("indice.bin", "rb") as archivo:
    with mmap.mmap(
        archivo.fileno(),
        0,
        access=mmap.ACCESS_READ,
    ) as mapa:
        posicion = mapa.find(b"CLAVE=")
        print(posicion)

Intentar modificar un mapa read-only genera error. La restricción reduce el impacto de bugs y comunica mejor la intención.

Índices y slices

El objeto se comporta de forma parecida a una secuencia mutable de bytes. Un índice devuelve un entero y un slice devuelve bytes.

primero = mapa[0]
header = mapa[0:32]

Los slices grandes todavía copian datos. Usa memoryview para integrar APIs compatibles sin una copia adicional y controla cuidadosamente su lifecycle.

Cursor interno

Además del acceso indexado, mmap ofrece métodos como read(), readline(), seek() y tell().

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

El cursor es estado mutable compartido. Dos partes del programa pueden interferirse. Prefiere offsets explícitos o sincronización.

Busca patrones de bytes

find() y rfind() buscan secuencias sin crear primero otra copia completa del archivo.

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

El patrón es útil en logs y formatos binarios, pero el texto todavía exige encoding y límites correctos.

Texto y encoding

El mapa contiene bytes. Decodifica solo la región necesaria.

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

Un slice puede cortar un carácter multibyte. Busca delimitadores en bytes o usa un decoder incremental al procesar chunks.

Modifica bytes en el lugar

Un mapa escribible puede sustituir bytes sin reescribir todo el archivo.

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

La cantidad asignada debe coincidir con la región reemplazada. Un objeto mmap no crece como una lista.

Flush y durabilidad

flush() solicita que las páginas modificadas sean escritas. La durabilidad exacta depende del sistema, filesystem, cache del dispositivo y flags.

Para datos críticos, combina el diseño con archivo temporal, rename atómico, fsync() y un protocolo transaccional cuando corresponda. Un flush no convierte varias escrituras en una transacción.

ACCESS_COPY

ACCESS_COPY crea una vista privada copy-on-write. El proceso observa sus cambios, pero el archivo original no se modifica.

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

Es útil para transformaciones temporales. Las páginas modificadas pueden aumentar el uso de memoria.

Mapea solo una región

Los archivos enormes pueden procesarse mediante ventanas pequeñas. El offset debe respetar la granularidad de asignación de la plataforma.

granularidad = mmap.ALLOCATIONGRANULARITY
offset = (inicio // granularidad) * granularidad
delta = inicio - offset
longitud = delta + tamano

with mmap.mmap(
    archivo.fileno(),
    longitud,
    access=mmap.ACCESS_READ,
    offset=offset,
) as mapa:
    datos = mapa[delta:delta + tamano]

Los offsets desalineados son una causa frecuente de errores.

Archivos mayores que la RAM

El sistema puede mapear un archivo mayor que la memoria física porque carga páginas bajo demanda. Sin embargo, un recorrido aleatorio puede producir paging intenso y thrashing.

Prefiere acceso secuencial, mide memoria residente y page faults y evita mantener muchos mapas gigantes abiertos.

Redimensionamiento

resize() puede cambiar el tamaño en determinadas plataformas y modos. El soporte varía.

Para código portable, cierra el mapa, redimensiona el archivo y crea uno nuevo. Recalcula todos los offsets.

Crea un archivo con tamaño previo

El archivo debe contener suficientes bytes antes de escribir en regiones futuras.

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

Los archivos sparse y la reserva física dependen del filesystem. truncate() no garantiza que todos los bloques estén asignados.

Mapeos anónimos

En plataformas compatibles, un mapa puede crearse sin archivo como buffer de trabajo o memoria compartida.

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

Las reglas de nombre y compartición difieren entre Windows y Unix. Evalúa también multiprocessing.shared_memory.

Compartir entre procesos

Procesos que mapean la misma región pueden observar cambios compartidos según el modo. El mapeo no sincroniza operaciones.

Usa locks, semáforos, marcadores de versión, checksums y un protocolo de publicación. Un lector no debe analizar una estructura mientras el escritor la modifica parcialmente.

Threads y estado compartido

Las lecturas indexadas independientes pueden ser sencillas, pero operaciones con seek() y read() requieren coordinación cuando se comparte el objeto.

Protege escrituras concurrentes o centraliza los cambios. La ausencia de excepción no garantiza consistencia.

Cambios externos del archivo

Si otro proceso trunca un archivo mapeado, un acceso posterior puede provocar errores graves, señales del sistema o referencias inválidas.

Define ownership y prohíbe redimensionar mientras existan lectores. Para actualizaciones completas, crea un archivo nuevo y sustitúyelo de forma atómica.

Buffer protocol y memoryview

Un memoryview permite que bibliotecas compatibles consuman bytes sin otra copia.

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

El mapa no puede cerrarse mientras existan views exportadas. Libéralas explícitamente.

Integración con struct

Los formatos binarios de layout fijo pueden decodificarse directamente con struct.unpack_from().

import struct

version, longitud = struct.unpack_from("!HI", mapa, 0)

Comprueba el tamaño mínimo y no confíes en longitudes leídas de archivos externos.

Rendimiento

Compara mmap con lectura por chunks, readinto() y APIs de alto nivel. Para recorridos secuenciales simples, la lectura buffered puede ser igual de rápida y más fácil.

Mide tiempo total, memoria, page faults y carga real. Un benchmark con el archivo ya en cache puede engañar.

Seguridad

Un archivo mapeado sigue siendo entrada no confiable. Valida magic bytes, firma, tamaños, contadores, offsets y límites antes de indexar posiciones calculadas.

No uses valores del archivo en aritmética de slices sin límites. Un archivo malicioso puede provocar consumo excesivo o acceso inválido.

Manejo de errores

Apertura, mapeo, flush e índices pueden lanzar OSError, ValueError, TypeError o errores de índice.

try:
    with open(ruta, "rb") as archivo:
        with mmap.mmap(archivo.fileno(), 0, access=mmap.ACCESS_READ) as mapa:
            analizar(mapa)
except (OSError, ValueError) as error:
    raise RuntimeError(f"no se pudo mapear {ruta}") from error

Incluye ruta y operación en el diagnóstico sin exponer datos sensibles.

Pruebas

Prueba archivo vacío, tamaño mínimo, truncamiento, permisos, offsets desalineados, cambios concurrentes, estructuras inválidas, límites, flush, Windows y Unix.

Usa archivos temporales reales. Los mocks no reproducen alineación ni semántica del sistema operativo.

Errores comunes

Los fallos frecuentes son mapear archivos vacíos, abrir con modo incompatible, olvidar alineación, suponer que los slices no copian, cerrar con memoryview activo, redimensionar externamente, tratar flush() como transacción y usar acceso aleatorio sin medir paging.

Conclusión

mmap convierte archivos y regiones compartidas en buffers direccionables. Es especialmente útil para búsqueda en archivos grandes, formatos binarios, acceso aleatorio e integración con APIs de buffer.

Mantén ownership explícito, valida límites, sincroniza escritores y compara con I/O buffered. Consulta la documentación oficial de mmap, multiprocessing en Python y contextlib en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Close-up of a man with binary code projected on his face, symbolizing cybersecurity.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    struct en Python: datos binarios

    Aprende struct en Python para empacar datos binarios, controlar endianness, offsets, padding, buffers, sockets y validación segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Close-up of a hand pointing at audio editing software on a monitor in a recording studio.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    importlib.metadata: versiones y plugins

    Aprende importlib.metadata en Python para consultar versiones, requisitos, archivos, distribuciones, entry points y plugins sin importar paquetes.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    A public library bookshelf displaying a variety of books and DVDs, providing a cozy reading atmosphere.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources: lee archivos de paquetes

    Aprende importlib.resources en Python para leer templates y datos con Traversable, files y as_file en wheels, ZIPs y aplicaciones frozen.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    runpy en Python: ejecuta módulos y scripts

    Aprende runpy en Python para ejecutar módulos y scripts, controlar __main__, run_path, alter_sys, namespaces, tests y aislamiento.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Close-up of a python snake coiled in darkness, showcasing its scales and eyes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil en Python: descubre paquetes

    Aprende pkgutil en Python para listar módulos, recorrer paquetes, descubrir plugins, consultar importers y leer recursos con seguridad.

    Ler mais

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

    modulefinder en Python: descubre imports

    Aprende modulefinder en Python para descubrir imports, dependencias transitivas, módulos ausentes, paths, plugins y límites del análisis estático.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026