copyreg en Python: personaliza pickle

Publicado el: 27/08/2026
Tempo de leitura: 6 minutos
Vibrant assortment of pickled vegetables in jars with red fabric covers, displayed on shelves.

El módulo copyreg permite registrar funciones utilizadas por pickle para serializar tipos que no definen su propia reducción. Es especialmente útil para extensiones nativas, clases de terceros, wrappers y objetos cuya forma serializada debe definirse fuera de la clase.

En clases propias, métodos como __reduce__(), __reduce_ex__(), __getstate__() y __setstate__() suelen ser más fáciles de encontrar y mantener. Usa copyreg cuando el registro externo sea una decisión deliberada. Recuerda además que pickle no es seguro para entrada no confiable: cargar un payload puede ejecutar código.

Cómo reconstruye objetos pickle

Pickle no guarda una imagen cruda de la memoria. Describe cómo reconstruir un objeto mediante un callable, argumentos, estado opcional, items de secuencia y pares de mapping.

Una función de reducción simple devuelve un callable y una tupla de argumentos.

class Punto:
    def __init__(self, x, y):
        self.x = x
        self.y = y


def reconstruir_punto(x, y):
    return Punto(x, y)


def reducir_punto(obj):
    return reconstruir_punto, (obj.x, obj.y)

copyreg.pickle() asocia la función con el tipo.

Registra un tipo

import copyreg
import pickle

copyreg.pickle(Punto, reducir_punto)

datos = pickle.dumps(Punto(2, 5))
restaurado = pickle.loads(datos)
print(restaurado.x, restaurado.y)

El registro es global al proceso y afecta serializaciones posteriores. Realízalo durante una inicialización predecible.

El constructor debe ser importable

Las funciones de reconstrucción deben poder importarse por su nombre de módulo cuando el pickle se carga en otro proceso. Funciones locales, lambdas y closures normalmente fallan.

Define reconstructores a nivel de módulo y conserva rutas estables. Mover o renombrar una función puede romper datos antiguos.

Registra una sola vez

La función reductora debe permanecer disponible durante la serialización. Evita registrar dentro de requests o scopes temporales.

def configurar_serializacion():
    copyreg.pickle(Punto, reducir_punto)

Llama el setup durante la inicialización del paquete o proceso.

Estado adicional

Una tupla de reducción puede incluir un tercer elemento con estado. Tras construir la instancia, pickle aplica ese estado mediante __setstate__() o actualizando __dict__.

def reducir_documento(obj):
    estado = {"titulo": obj.titulo, "tags": obj.tags}
    return Documento, (), estado

El callable de reconstrucción debe aceptar los argumentos proporcionados.

Objetos con __slots__

Las clases con slots pueden necesitar estado explícito porque no utilizan un diccionario de instancia normal.

def reducir_usuario(obj):
    return crear_usuario, (obj.id, obj.nombre)

No dependas de detalles privados del layout. Define una representación lógica y estable.

Tipos de extensiones nativas

copyreg es común cuando un tipo viene de C o de un paquete externo y no puede modificarse. Un reducer Python extrae valores y elige un callable de reconstrucción.

Handles, punteros, conexiones y recursos del sistema no deberían serializarse como si fueran portables. Conserva datos o identificadores.

No serialices recursos activos

Sockets, archivos abiertos, locks, threads, procesos y conexiones de base no pueden reconstruirse con seguridad desde estado crudo.

Serializa configuración o IDs. El proceso restaurado abre un recurso nuevo y maneja fallos de conexión.

Compatibilidad entre versiones

Los pickles pueden permanecer almacenados durante años. Cambiar callable, argumentos o estado puede volver ilegible el contenido anterior.

Incluye una versión explícita y conserva migraciones.

estado = {
    "version": 2,
    "nombre": obj.nombre,
    "opciones": obj.opciones,
}

El reconstructor o __setstate__() puede aceptar versiones anteriores.

Protocolos de pickle

El protocolo influye en eficiencia y recursos disponibles. Una función registrada con copyreg no recibe automáticamente el número del protocolo.

Si el comportamiento debe variar, __reduce_ex__(protocol) en la clase puede ser más apropiado.

copyreg frente a métodos de clase

Para una clase propia, mantener el comportamiento dentro de ella suele mejorar la descubribilidad.

class Punto:
    def __reduce__(self):
        return type(self), (self.x, self.y)

Usa registro externo para integrar tipos de terceros, separar políticas o no modificar una API pública.

dispatch_table personalizada

Un Pickler personalizado puede utilizar su propia dispatch_table y evitar cambios globales.

import copyreg
import io
import pickle

buffer = io.BytesIO()
pickler = pickle.Pickler(buffer)
pickler.dispatch_table = copyreg.dispatch_table.copy()
pickler.dispatch_table[Punto] = reducir_punto
pickler.dump(Punto(1, 2))

Es preferible cuando bibliotecas distintas requieren políticas diferentes.

Evita conflictos globales

Dos paquetes pueden registrar reducers distintos para el mismo tipo. El último registro puede alterar todo el proceso.

Las bibliotecas reutilizables deberían preferir tablas locales. Si el registro global es inevitable, documéntalo y añade tests de integración.

constructor

copyreg.constructor() marca un callable como constructor para ciertos mecanismos históricos. Su uso directo es raro en código moderno.

La palabra “constructor” no vuelve segura la carga. La deserialización sigue pudiendo ejecutar callables.

Códigos de extensión

add_extension(), remove_extension() y clear_extension_cache() administran códigos numéricos compactos para referencias globales.

import copyreg

copyreg.add_extension("mipaquete.modelos", "Punto", 1001)

Los códigos pertenecen a un registro global y deben coordinarse. Las colisiones causan errores o reconstrucción incorrecta.

Gobernanza de códigos

No elijas números aleatorios en bibliotecas distribuidas sin una política. Un código debe identificar siempre el mismo módulo y nombre.

Eliminar o reutilizar un código vuelve ambiguos los pickles antiguos.

Cache de extensiones

El unpickler puede cachear la resolución. clear_extension_cache() limpia ese estado, principalmente en tests.

No lo limpies repetidamente en producción: perjudica rendimiento y no resuelve compatibilidad.

Seguridad de pickle

Nunca pases datos de usuarios, red, archivos no confiables o buckets compartidos a pickle.loads(). El formato puede solicitar llamadas a funciones.

Una firma criptográfica solo verifica origen e integridad cuando claves y emisor son confiables. Para interoperabilidad, prefiere JSON, MessagePack u otro formato sin ejecución automática.

Valida después de cargar

Incluso un pickle confiable puede estar antiguo o corrupto. Valida tipos, límites, versión e invariantes.

No supongas argumentos razonables. Un payload manipulado dentro de un entorno confiable puede consumir demasiados recursos.

Límites de tamaño

Pickle puede representar estructuras enormes y profundas. Limita el tamaño del archivo antes de cargar y usa límites de CPU y memoria.

No existe un argumento universal de profundidad segura para loads().

Multiprocessing

Los pools de procesos serializan tareas y resultados. Un registro de copyreg puede permitir transferir un tipo, pero los workers también deben importar la configuración.

Coloca el setup en un módulo importable y prueba con spawn. Consulta multiprocessing en Python.

Objetos grandes

Una reducción personalizada puede disminuir el payload al guardar solo estado esencial. Mide tamaño y tiempo.

No sacrifiques claridad y compatibilidad por una micro-optimización. Algo de redundancia puede facilitar migraciones.

Herencia

Un registro para una clase no debe asumirse correcto para todas las subclases. Pueden añadir estado o invariantes.

Prueba el tipo exacto y decide si cada subclase necesita su propio reducer.

Dataclasses y named tuples

Muchos tipos comunes ya se serializan correctamente. No registres una función solo porque un objeto tiene varios campos.

Usa copyreg cuando la serialización predeterminada sea imposible, inestable o incorrecta.

Tests de round trip

Un test básico serializa, deserializa y compara estado.

original = Punto(2, 5)
restaurado = pickle.loads(pickle.dumps(original))
assert (restaurado.x, restaurado.y) == (2, 5)

Incluye protocolos distintos, procesos separados, versiones antiguas y estado inválido.

Prueba en un proceso limpio

Un round trip en el mismo proceso puede ocultar imports y registros ya cargados. Lee el contenido en un subprocess limpio.

Así aparecen funciones locales, rutas de módulo incorrectas e inicialización ausente.

Observabilidad

Registra tipo, versión del estado, protocolo, tamaño y duración sin guardar el pickle completo. Puede contener secretos.

Los fallos de reconstrucción deben incluir un ID del objeto o job, no los bytes.

Errores comunes

Los fallos frecuentes son registrar lambdas, mover el reconstructor sin migración, serializar handles activos, cargar datos no confiables, alterar el registro global desde una biblioteca, ignorar subclases, reutilizar códigos y probar solo en el mismo proceso.

Conclusión

copyreg enseña a pickle cómo reducir y reconstruir tipos que no controlan su serialización. Usa funciones estables a nivel de módulo, estado versionado, tablas locales cuando sea posible y tests en procesos limpios.

No uses pickle para entrada no confiable. Consulta la documentación oficial de copyreg y la documentación de pickle.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    reprlib en Python: objetos resumidos

    Aprende reprlib en Python para resumir listas, strings y objetos recursivos, limitar logs y crear representaciones seguras y legibles.

    Ler mais

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

    graphlib en Python: orden topológico

    Aprende graphlib en Python para ordenar dependencias, detectar ciclos, ejecutar tareas listas en paralelo y crear pipelines seguros.

    Ler mais

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

    weakref: evita retener objetos en cachés

    Aprende weakref en Python para referencias débiles, caches, WeakSet, WeakMethod, finalize, callbacks y evitar retención accidental.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    A top view of stacked timber logs showcasing natural textures and patterns.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextlib en Python: gestiona recursos

    Aprende contextlib en Python con contextmanager, ExitStack, suppress, closing, asynccontextmanager y cleanup seguro de recursos.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Close-up of a computer screen displaying colorful programming code with depth of field.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ast en Python: analiza código fuente

    Aprende ast en Python para analizar y transformar código, crear visitors, conservar posiciones, usar literal_eval y evitar riesgos de ejecución.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Close-up of electric plug and socket with vibrant lighting, showcasing technology and energy concepts.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    socket en Python: redes TCP y UDP

    Aprende socket en Python para clientes y servidores TCP y UDP, framing, timeouts, IPv6, concurrencia, TLS y seguridad de red.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026