copyreg en Python: personaliza pickle

Publicado el: 06/08/2026
Tempo de leitura: 6 minutos
Monitor con código binario que representa personalización de pickle con copyreg en Python

El módulo pickle puede serializar muchos tipos built-in y clases definidas normalmente, pero algunas extensiones, objetos inmutables y clases controladas por bibliotecas necesitan una regla externa de reconstrucción. El módulo copyreg en Python registra funciones de reducción para tipos, enseñando a pickle cómo guardar objetos que no pueden o no deben implementar directamente __reduce__().

Esta guía explica copyreg.pickle(), funciones de reconstrucción, compatibilidad, códigos de extensión y límites de seguridad. Complementa nuestros artículos sobre pickle en Python, pickletools, copy, shelve e inspect.

Qué hace una función de reducción

Para reconstruir un objeto, pickle necesita una receta compacta que indique qué callable debe ejecutarse y qué argumentos debe recibir.

def reducir_objeto(objeto):
    return (reconstruir_objeto, (objeto.valor,))

El resultado más común es una tupla con callable y argumentos. Los protocolos avanzados también admiten estado, iteradores y buffers.

Cuándo usar copyreg

Copyreg es útil cuando:

  • el tipo pertenece a una biblioteca que no puedes modificar;
  • el objeto está implementado por una extensión C;
  • la política de serialización debe quedar fuera de la clase;
  • varias versiones necesitan una estrategia centralizada;
  • el soporte debe registrarse durante el startup.

Cuando la clase es tuya y la serialización forma parte de su contrato, __reduce__(), __getstate__() y __setstate__() pueden ser más claros.

Registrar un tipo

La función principal es copyreg.pickle(type, function).

import copyreg
import pickle

class Punto:
    __slots__ = ("x", "y")

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


def reducir_punto(punto):
    return (Punto, (punto.x, punto.y))

copyreg.pickle(Punto, reducir_punto)

datos = pickle.dumps(Punto(10, 20))
restaurado = pickle.loads(datos)

Después del registro, los picklers comunes consultan la tabla global para instancias de ese tipo exacto.

Función de reconstrucción separada

El callable devuelto no tiene que ser la propia clase.

def reconstruir_punto(x, y):
    punto = Punto.__new__(Punto)
    punto.x = x
    punto.y = y
    return punto


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

Esto ayuda cuando el constructor público valida entrada, exige dependencias o produce efectos que no deberían ocurrir durante la restauración.

El callable debe seguir siendo importable

Pickle normalmente guarda una referencia global. La función de reconstrucción debería estar en el nivel del módulo y ser importable con el mismo nombre durante el unpickle.

Lambdas, funciones locales y closures son malas opciones. Renombrar o mover la función puede romper archivos antiguos.

Compatibilidad de rutas

Si el código cambia de paquete, conserva un alias en la ruta anterior o migra los datos antes de eliminar el símbolo.

# modulo_antiguo.py
from paquete_nuevo.serializacion import reconstruir_punto

Planifica la vida útil de los datos. Para registros que deben durar años o cruzar lenguajes, un esquema explícito suele ser mejor.

Registrar constructores antiguos

copyreg.constructor() declara que un objeto puede ser usado como constructor por mecanismos históricos de extensión.

copyreg.constructor(reconstruir_punto)

La función comprueba que el argumento sea callable y genera TypeError en caso contrario. Las aplicaciones modernas rara vez la necesitan.

Códigos de extensión

add_extension() relaciona un par módulo/nombre con un código entero usado por opcodes de extensión.

copyreg.add_extension(
    "mi_paquete.serializacion",
    "reconstruir_punto",
    1001,
)

El código debe estar entre 1 y 0x7fffffff y ser globalmente único en el ecosistema que intercambia esos pickles.

Eliminar una extensión

copyreg.remove_extension(
    "mi_paquete.serializacion",
    "reconstruir_punto",
    1001,
)

Los tres valores deben coincidir. Los pickles que dependen del código dejan de funcionar después de retirarlo.

Limpiar el cache de extensiones

clear_extension_cache() vacía el cache interno de extensiones resueltas.

copyreg.clear_extension_cache()

Se utiliza principalmente en tests y entornos dinámicos. Las aplicaciones normales deberían registrar una vez durante el startup.

Registro global

copyreg.pickle() cambia una tabla global del proceso. Cualquier operación pickle posterior puede observar la regla.

Evita plugins que registren comportamientos diferentes para el mismo tipo. Centraliza la política y documenta su propietario.

Dispatch table privada

Cuando una regla no debe ser global, crea un pickle.Pickler personalizado con una copia de la tabla.

import copyreg
import io
import pickle

class PicklerLocal(pickle.Pickler):
    dispatch_table = copyreg.dispatch_table.copy()

PicklerLocal.dispatch_table[Punto] = reducir_punto

buffer = io.BytesIO()
PicklerLocal(buffer).dump(Punto(1, 2))

El patrón aísla políticas entre subsistemas y tests.

Estado adicional

Una reducción puede incluir estado además de argumentos del constructor.

def reducir_sesion(objeto):
    return (
        reconstruir_sesion,
        (objeto.identificador,),
        {"preferencias": objeto.preferencias},
    )

El objeto restaurado recibe estado mediante __setstate__() o actualización de su diccionario. Prueba con cuidado las clases que usan __slots__.

No serializar recursos vivos

Sockets, locks, archivos abiertos, threads, conexiones de base y clientes de red no deberían restaurarse como si conservaran el mismo recurso.

Serializa configuración o identificadores declarativos y reconecta explícitamente en el nuevo entorno.

Versionar el estado

Incluye una versión cuando la estructura puede evolucionar.

def reducir_config(config):
    estado = {
        "version": 2,
        "datos": config.datos,
    }
    return (reconstruir_config, (estado,))

La función de reconstrucción puede migrar versiones anteriores y rechazar futuras desconocidas.

Validación al reconstruir

Incluso los datos internos pueden corromperse. Valida tipos, límites y campos obligatorios.

def reconstruir_config(estado):
    if not isinstance(estado, dict):
        raise TypeError("estado inválido")
    if estado.get("version") not in {1, 2}:
        raise ValueError("versión no soportada")
    return Config(migrar(estado))

La validación mejora robustez, pero no hace seguro un pickle de terceros.

Límite de seguridad

La documentación oficial de copyreg describe su integración con pickle. Como pickle puede importar y llamar funciones, cada reducer añade una ruta de reconstrucción.

Nunca hagas unpickle de datos no confiables. Un reducer seguro para tu tipo no impide que el flujo contenga otras operaciones maliciosas.

Autenticación e integridad

Para archivos generados internamente, usa HMAC o firma para detectar cambios antes de cargar. Conserva la clave separada.

La autenticación confirma un productor esperado, pero no sustituye control de acceso, rotación de claves ni compatibilidad.

Interacción con copy

El módulo copy utiliza protocolos relacionados para copia superficial y profunda. Un registro puede influir en cómo se copia el objeto.

Prueba pickle.dumps(), copy.copy() y copy.deepcopy() cuando el tipo participa en los tres flujos.

Tests de round-trip

def test_punto_round_trip():
    original = Punto(3, 4)
    restaurado = pickle.loads(pickle.dumps(original))

    assert restaurado.x == 3
    assert restaurado.y == 4
    assert restaurado is not original

Prueba protocolos soportados, estados vacíos, valores extremos y versiones antiguas.

Inspeccionar con pickletools

Usa pickletools.dis() para confirmar qué globals y opcodes se guardaron sin ejecutar el flujo.

import pickletools
pickletools.dis(pickle.dumps(Punto(1, 2)))

Esto revela dependencias accidentales de rutas internas y protocolos demasiado nuevos.

Errores frecuentes

  • Registrar lambda o función local.
  • Mover el reconstructor sin migración.
  • Usar códigos de extensión en conflicto.
  • Modificar el registro global en cada request.
  • Intentar persistir recursos vivos.
  • No versionar estado duradero.
  • Suponer que validar vuelve confiable pickle.
  • Ignorar efectos sobre copy y deepcopy.

Buenas prácticas

  • Usa funciones de módulo.
  • Centraliza registros durante el startup.
  • Prefiere tablas privadas cuando sea posible.
  • Serializa solo estado declarativo.
  • Versiona y valida la reconstrucción.
  • Conserva aliases para datos antiguos.
  • Prueba protocolos y copias.
  • Nunca cargues pickle de origen desconocido.

Conclusión

El módulo copyreg en Python registra reglas externas de reducción para tipos que necesitan participar en pickle sin modificar sus clases. Es útil para extensiones, clases de terceros y políticas centralizadas.

La flexibilidad exige disciplina. Los callables deben seguir importables, el estado debe versionarse y el registro global debe controlarse. Copyreg personaliza cómo se persisten objetos confiables; no convierte pickle en un formato seguro para entrada externa.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Documento y bandeja de entrada que representan buzones de correo con mailbox en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox en Python: buzones de correo

    Aprende mailbox en Python para leer, crear y migrar Maildir, mbox y MH con locking, flags, mensajes y manejo seguro

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Editor de texto que representa formato con textwrap en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap en Python: formatea textos

    Aprende textwrap en Python para dividir, rellenar, acortar, indentar y quitar sangrías con control de ancho, espacios y palabras largas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Carpeta y lupa que representan filtros de nombres con fnmatch en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch en Python: filtra nombres de archivos

    Aprende fnmatch en Python para filtrar nombres de archivos con comodines, controlar mayúsculas, excluir patrones y distinguir glob de regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor con datos binarios que representa arrays numéricos compactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar archivos binarios, byte order, memoryview y buffers seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Círculo cromático que representa conversiones RGB, HSV y HLS con colorsys en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys en Python: RGB, HSV y HLS

    Aprende colorsys en Python para convertir colores entre RGB, HSV, HLS y YIQ, crear paletas y evitar errores de escala

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Icono de configuración que representa archivos plist con plistlib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib en Python: archivos plist

    Aprende plistlib en Python para leer y escribir archivos plist XML y binarios, validar datos y manejar fechas, bytes y

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026