marshal en Python: serialización interna

Publicado el: 06/08/2026
Tempo de leitura: 6 minutos
Datos binarios que representan serialización interna con marshal en Python

El intérprete Python necesita una forma rápida de guardar y cargar algunas estructuras internas, especialmente objetos de código usados en archivos .pyc. El módulo marshal en Python expone este formato binario de bajo nivel para valores simples y datos del intérprete. Puede ayudar en herramientas especializadas, pero no fue diseñado como formato general de persistencia, intercambio o entrada externa.

Esta guía explica dump(), load(), dumps(), loads(), versiones de formato y bloqueo de code objects. Complementa nuestros artículos sobre pickle, pickletools, py_compile, bytecode con dis y comparación de archivos.

Para qué existe marshal

Marshal fue creado para necesidades internas de Python. Su propósito principal es ayudar al intérprete a leer y escribir estructuras internas, no proporcionar un protocolo estable para aplicaciones.

A diferencia de JSON, no prioriza interoperabilidad. A diferencia de pickle, no intenta serializar instancias arbitrarias. La documentación advierte que los detalles pueden cambiar entre versiones.

Serializar en memoria

marshal.dumps() convierte un valor soportado en bytes.

import marshal

objeto = {
    "nombre": "Ana",
    "puntos": [10, 20, 30],
}

datos = marshal.dumps(objeto)
print(type(datos), len(datos))

El resultado es binario y no debería editarse manualmente.

Restaurar desde bytes

marshal.loads() lee un valor.

restaurado = marshal.loads(datos)
print(restaurado)

Utiliza solamente bytes producidos por un entorno confiable y compatible. La documentación oficial de marshal advierte contra datos no confiables.

Escribir en archivo

dump() escribe en un archivo binario.

with open("datos.marshal", "wb") as archivo:
    marshal.dump(objeto, archivo)

Si existe un tipo no soportado, genera ValueError. Parte de datos inválidos puede haber sido escrita, por lo que no debes reutilizar el destino tras el fallo.

Escritura atómica

Escribe en un temporal y reemplaza el destino solo después del éxito.

from pathlib import Path

ruta = Path("datos.marshal")
temporal = ruta.with_suffix(".tmp")

with temporal.open("wb") as archivo:
    marshal.dump(objeto, archivo)

temporal.replace(ruta)

Esto reduce corrupción por errores de serialización, aunque la durabilidad ante pérdida de energía puede requerir flush y sincronización.

Leer desde archivo

load() lee un valor desde la posición actual.

with open("datos.marshal", "rb") as archivo:
    objeto = marshal.load(archivo)

Los bytes posteriores permanecen sin leer. Esto permite concatenar valores, pero la aplicación necesita contador o framing.

Varios valores consecutivos

with open("secuencia.marshal", "wb") as archivo:
    marshal.dump({"id": 1}, archivo)
    marshal.dump({"id": 2}, archivo)

with open("secuencia.marshal", "rb") as archivo:
    primero = marshal.load(archivo)
    segundo = marshal.load(archivo)

Sin framing, puede ser difícil distinguir final válido de truncamiento.

Tipos soportados

Marshal maneja varios tipos simples, incluidos:

  • None, booleanos y números;
  • strings y bytes;
  • tuplas, listas y diccionarios;
  • sets y frozensets;
  • estructuras adicionales según la versión;
  • objetos de código cuando están permitidos.

Las instancias arbitrarias de clases no se soportan como en pickle.

Contenedores recursivos

Las versiones modernas pueden representar determinados contenedores recursivos.

lista = []
lista.append(lista)

datos = marshal.dumps(lista)
restaurada = marshal.loads(datos)
assert restaurada[0] is restaurada

Las estructuras extremadamente profundas todavía pueden alcanzar límites. No proceses anidamiento sin control.

Versión del formato

El módulo expone marshal.version, versión predeterminada actual.

print(marshal.version)

datos = marshal.dumps(objeto, marshal.version)

El argumento no garantiza compatibilidad completa entre intérpretes. Un formato reconocido puede contener un tipo cuya representación cambió.

Formato no equivale a versión de Python

El número de formato marshal es independiente de la release de Python. Los objetos de código pueden cambiar aunque el contenedor se reconozca.

Registra implementación, versión completa, plataforma y formato cuando importa la compatibilidad.

Objetos de código

Los code objects contienen bytecode, constantes, nombres y metadatos.

codigo = compile("resultado = 2 + 2", "<ejemplo>", "exec")
datos = marshal.dumps(codigo)
restaurado = marshal.loads(datos)

Ejecutar el objeto restaurado sigue siendo ejecución de código.

ambiente = {}
exec(restaurado, ambiente)
print(ambiente["resultado"])

Nunca ejecutes code objects de origen desconocido.

Bloquear code objects

Las versiones recientes ofrecen allow_code.

datos = marshal.dumps(
    objeto,
    allow_code=False,
)

restaurado = marshal.loads(
    datos,
    allow_code=False,
)

Cuando es falso, se rechazan objetos de código. Esto elimina una categoría, pero no vuelve seguros bytes hostiles.

Compatibilidad de code objects

La documentación advierte que su formato no es compatible entre versiones. Cargar uno incorrecto tiene comportamiento indefinido.

No los guardes como caché duradero. Usa el mecanismo oficial de .pyc con tags y encabezados.

marshal y pyc

Aunque los .pyc usan marshal para el code object, también incluyen un encabezado gestionado por importlib. marshal.dumps(codigo) por sí solo no crea un pyc válido.

Usa py_compile o compileall.

marshal frente a pickle

Pickle soporta clases personalizadas y protocolos de reconstrucción, pero puede invocar callables. Marshal soporta menos tipos y sigue siendo inseguro para datos no confiables.

Ninguno es adecuado para uploads externos. Prefiere JSON, base de datos o formato con esquema.

marshal frente a JSON

JSON ofrece interoperabilidad, texto legible y modelo restringido. Marshal es específico de Python e interno.

Aunque pueda ser más pequeño, la falta de estabilidad suele costar más en persistencia.

Detectar truncamiento

Un archivo incompleto puede generar EOFError, ValueError u otro error.

try:
    with open("datos.marshal", "rb") as archivo:
        objeto = marshal.load(archivo)
except (EOFError, ValueError, TypeError) as error:
    print("Archivo inválido:", error)

No utilices un valor parcialmente leído.

Integridad externa

Los datos internos pueden acompañarse de hash o HMAC.

import hashlib

digest = hashlib.sha256(datos).hexdigest()

Un hash detecta corrupción accidental, pero no un atacante que puede reemplazar ambos. Usa HMAC con clave secreta para autenticidad.

Límites de recursos

Datos malformados pueden intentar crear estructuras grandes o profundas. Limita tamaño antes de cargar y procesa entradas hostiles en subprocess con memoria y tiempo restringidos.

from pathlib import Path

ruta = Path("datos.marshal")
if ruta.stat().st_size > 10_000_000:
    raise ValueError("archivo demasiado grande")

La verificación de tamaño no sustituye aislamiento.

Eventos de auditoría

Las operaciones generan eventos de auditoría de Python. Los runtimes embebidos pueden observar cargas y escrituras.

No dependas solo de hooks para aceptar entrada peligrosa. La validación de origen debe ocurrir antes.

Uso en tests y herramientas

Marshal puede servir en tests del intérprete, estudios de formato y herramientas con artefactos efímeros de la misma versión.

Documenta que el archivo es descartable y se regenera al cambiar de runtime.

Test de round-trip

def test_round_trip():
    original = {
        "ids": [1, 2, 3],
        "activo": True,
    }
    restaurado = marshal.loads(marshal.dumps(original))
    assert restaurado == original

Prueba tipos soportados, vacío, truncamiento, versión y allow_code=False.

Errores frecuentes

  • Usar marshal como base duradera.
  • Cargar bytes de usuarios.
  • Suponer compatibilidad entre versiones.
  • Ejecutar code objects restaurados.
  • Conservar archivo parcial tras ValueError.
  • Confundir datos marshal con pyc completo.
  • No limitar tamaño y profundidad.
  • Tratar allow_code=False como sandbox.

Buenas prácticas

  • Usa marshal solo para necesidades internas y efímeras.
  • Registra versiones de Python y formato.
  • Desactiva code objects si no son necesarios.
  • Escribe mediante temporal y reemplazo.
  • Comprueba tamaño antes de cargar.
  • Autentica artefactos internos cuando corresponda.
  • Regenera datos al cambiar de runtime.
  • Elige formatos estables para datos de aplicación.

Conclusión

El módulo marshal en Python ofrece serialización binaria rápida para un conjunto de tipos y estructuras internas. Ayuda a comprender parte de los cachés de bytecode y puede apoyar herramientas especializadas.

Su contrato es estrecho: el formato no es estable para persistencia general, los code objects no son portables y los datos desconocidos no son seguros. Úsalo solo en entornos controlados, con artefactos descartables, límites y compatibilidad claramente definida.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Monitor con código binario que representa personalización de pickle con copyreg en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    copyreg en Python: personaliza pickle

    Aprende copyreg en Python para registrar funciones de reducción, personalizar pickle y preservar compatibilidad de objetos.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026
    Código Python que representa funciones especializadas con functools.partial
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    functools.partial en Python: guía práctica

    Aprende functools.partial en Python para fijar argumentos, adaptar callbacks y crear funciones especializadas claras.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    filecmp en Python: compara archivos y carpetas

    Aprende filecmp en Python para comparar archivos y carpetas con shallow, dircmp, cmpfiles, caché y hashes de integridad.

    Ler mais

    Tempo de leitura: 6 minutos
    03/08/2026
    Terminal de comandos que representa parsing seguro con shlex en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    shlex en Python: comandos seguros

    Aprende shlex en Python para separar comandos, tratar comillas, usar quote y join y reducir riesgos de inyección de shell.

    Ler mais

    Tempo de leitura: 6 minutos
    02/08/2026
    Base de datos local que representa persistencia con shelve en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    shelve en Python: persistencia simple

    Aprende shelve en Python para persistir objetos, actualizar datos mutables, evitar riesgos de pickle y decidir cuándo migrar a SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    01/08/2026
    Documentos de texto que representan comparación de versiones con difflib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    difflib en Python: compara textos y archivos

    Aprende difflib en Python para comparar textos, medir similitud, crear diffs unificados, informes HTML y sugerencias de nombres.

    Ler mais

    Tempo de leitura: 6 minutos
    01/08/2026