copyreg no Python: personalize o pickle

Publicado em: 06/08/2026
Tempo de leitura: 6 minutos
Monitor com código binário representando personalização de pickle com copyreg no Python

O módulo pickle sabe serializar muitos tipos built-in e classes definidas normalmente, mas algumas extensões, tipos imutáveis e objetos controlados por bibliotecas precisam de uma regra externa de reconstrução. O módulo copyreg no Python registra funções de redução associadas a tipos, permitindo ensinar o pickle a salvar objetos que não podem ou não devem implementar diretamente __reduce__().

Neste guia, você aprenderá a usar copyreg.pickle(), criar funções de reconstrução, preservar compatibilidade e compreender os limites de segurança. O conteúdo complementa nossos artigos sobre pickle no Python, pickletools, copy, shelve e inspect.

O que é uma função de redução

Para reconstruir um objeto, pickle precisa de uma descrição compacta: qual função deve ser chamada e quais argumentos devem ser fornecidos. Essa descrição é chamada de redução.

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

O resultado mais comum é uma tupla com callable e argumentos. Protocolos avançados também permitem estado adicional, iteradores e buffers.

Quando usar copyreg

Use copyreg quando:

  • o tipo pertence a uma biblioteca que você não pode modificar;
  • o objeto é implementado em C ou extensão;
  • a regra de serialização deve ficar fora da classe;
  • várias versões precisam compartilhar uma estratégia centralizada;
  • você deseja registrar suporte apenas durante a inicialização da aplicação.

Quando a classe é sua e a regra faz parte do contrato do tipo, métodos como __reduce__(), __getstate__() e __setstate__() podem ser mais claros.

Registrar um tipo

A função principal é copyreg.pickle(type, function).

import copyreg
import pickle

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

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


def reduzir_ponto(ponto):
    return (Ponto, (ponto.x, ponto.y))

copyreg.pickle(Ponto, reduzir_ponto)

dados = pickle.dumps(Ponto(10, 20))
restaurado = pickle.loads(dados)

A partir do registro, picklers comuns consultam a dispatch table global para instâncias daquele tipo.

Função de reconstrução separada

O callable retornado não precisa ser a própria classe.

def reconstruir_ponto(x, y):
    ponto = Ponto.__new__(Ponto)
    ponto.x = x
    ponto.y = y
    return ponto


def reduzir_ponto(ponto):
    return (reconstruir_ponto, (ponto.x, ponto.y))

Isso é útil quando o construtor público valida parâmetros, exige dependências ou produz efeitos que não devem ocorrer na restauração.

O callable deve ser importável

Pickle normalmente grava uma referência global ao callable. A função de reconstrução deve estar no nível do módulo e ser importável pelo mesmo nome durante o unpickle.

Lambdas, funções locais e closures não são escolhas adequadas. Renomear ou mover a função pode quebrar arquivos antigos.

Compatibilidade de módulos

Se o código muda de pacote, mantenha um alias no caminho antigo ou migre os dados antes de remover o símbolo.

# modulo_antigo.py
from pacote_novo.serializacao import reconstruir_ponto

Planeje a vida útil dos pickles. Para dados que precisam durar anos ou atravessar linguagens, um formato com esquema explícito geralmente é melhor.

Registrar construtores antigos

copyreg.constructor() declara que um objeto pode ser usado como construtor seguro pelo mecanismo de extensão antigo.

copyreg.constructor(reconstruir_ponto)

A função verifica se o argumento é chamável e gera TypeError caso contrário. A documentação informa que esse mecanismo existe por compatibilidade histórica e raramente é necessário em código moderno.

Códigos de extensão

add_extension() associa um par módulo/nome a um código inteiro usado por opcodes de extensão do pickle.

copyreg.add_extension(
    "meu_pacote.serializacao",
    "reconstruir_ponto",
    1001,
)

O código deve estar entre 1 e 0x7fffffff e ser globalmente único dentro do ecossistema que troca esses arquivos.

Remover uma extensão

copyreg.remove_extension(
    "meu_pacote.serializacao",
    "reconstruir_ponto",
    1001,
)

Os três valores precisam corresponder exatamente ao registro. Arquivos que dependem do código deixam de ser carregáveis depois da remoção.

Limpar o cache

clear_extension_cache() remove o cache interno de extensões resolvidas.

copyreg.clear_extension_cache()

A função é destinada principalmente a testes e cenários dinâmicos. Aplicações normais devem registrar extensões uma vez durante a inicialização.

Registro global

copyreg.pickle() altera uma tabela global do processo. Qualquer código que use pickle posteriormente pode observar a nova regra.

Evite registrar comportamentos diferentes para o mesmo tipo em plugins concorrentes. Centralize o registro em um módulo de inicialização e documente quem é responsável pela política.

Dispatch table privada

Quando uma regra não deve ser global, crie um pickle.Pickler personalizado com uma cópia da tabela.

import copyreg
import io
import pickle

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

PicklerLocal.dispatch_table[Ponto] = reduzir_ponto

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

Esse padrão isola políticas entre subsistemas e testes.

Estado adicional

Uma redução pode incluir estado além dos argumentos do construtor.

def reduzir_sessao(objeto):
    return (
        reconstruir_sessao,
        (objeto.identificador,),
        {"preferencias": objeto.preferencias},
    )

O objeto restaurado recebe o estado por __setstate__() quando disponível ou por atualização de __dict__. Teste cuidadosamente objetos com __slots__.

Não serializar recursos vivos

Sockets, locks, arquivos abertos, threads, conexões de banco e clientes de rede não devem ser restaurados como se ainda representassem o mesmo recurso.

Armazene somente configuração ou identificadores necessários e reconecte explicitamente no ambiente novo.

Versão do estado

Inclua uma versão quando a estrutura pode evoluir.

def reduzir_config(config):
    estado = {
        "versao": 2,
        "dados": config.dados,
    }
    return (reconstruir_config, (estado,))

A função de reconstrução pode migrar versões antigas e rejeitar versões futuras desconhecidas.

Validação na restauração

Mesmo dados internos podem estar corrompidos. Valide tipos, limites e campos obrigatórios na função de reconstrução.

def reconstruir_config(estado):
    if not isinstance(estado, dict):
        raise TypeError("estado inválido")
    if estado.get("versao") not in {1, 2}:
        raise ValueError("versão não suportada")
    return Config(migrar(estado))

Essa validação melhora robustez, mas não torna seguro carregar pickles de terceiros.

Segurança

A documentação oficial de copyreg descreve a integração com pickle. Como pickle pode importar e chamar funções, qualquer registro amplia os caminhos de reconstrução disponíveis.

Nunca faça unpickle de dados não confiáveis. Uma função de redução “segura” para seu tipo não impede que o mesmo arquivo contenha outras instruções maliciosas.

Assinatura e integridade

Para arquivos produzidos internamente, use HMAC ou assinatura para detectar alterações antes do unpickle. Mantenha a chave fora do arquivo e compare a assinatura de forma segura.

Autenticação só confirma a origem esperada. Ela não substitui controle de acesso, rotação de chaves e compatibilidade de versão.

copyreg e copy

O módulo copy utiliza protocolos relacionados para cópia rasa e profunda. Um registro pode influenciar como determinados objetos são copiados.

Teste tanto pickle.dumps() quanto copy.copy() e copy.deepcopy() quando o tipo participa dos dois fluxos.

Testar round-trip

def test_ponto_round_trip():
    original = Ponto(3, 4)
    restaurado = pickle.loads(pickle.dumps(original))

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

Teste todos os protocolos suportados, valores extremos, estado vazio e versões antigas.

Inspecionar com pickletools

Use pickletools.dis() para confirmar quais globals e opcodes foram gravados sem executar o arquivo.

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

Isso ajuda a detectar dependência acidental de caminhos internos e protocolos mais novos que o esperado.

Erros frequentes

  • Registrar lambda ou função local.
  • Mover o reconstrutor sem migração.
  • Usar códigos de extensão conflitantes.
  • Alterar a tabela global em cada requisição.
  • Tentar persistir recursos vivos.
  • Não versionar estados duradouros.
  • Presumir que validação torna pickle confiável.
  • Esquecer impactos em copy e deepcopy.

Boas práticas

  • Registre funções no nível do módulo.
  • Centralize registros durante o startup.
  • Use dispatch table privada quando possível.
  • Serialize apenas estado declarativo.
  • Versione e valide a reconstrução.
  • Mantenha aliases para dados antigos.
  • Teste protocolos e cópias.
  • Nunca carregue pickle de origem desconhecida.

Conclusão

O módulo copyreg no Python permite registrar regras externas de redução para tipos que precisam participar do protocolo pickle. Ele é útil para extensões, classes de terceiros e políticas de serialização centralizadas.

Essa flexibilidade exige disciplina. Callables precisam permanecer importáveis, estados devem ser versionados e a tabela global deve ser controlada. Copyreg personaliza como objetos confiáveis são persistidos; ele não transforma pickle em um formato seguro para entrada externa.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Documento e caixa de entrada representando caixas de e-mail com mailbox no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox no Python: caixas de e-mail

    Aprenda mailbox no Python para ler, criar e migrar caixas Maildir, mbox e MH com locking, mensagens, flags e tratamento

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026
    Editor de texto representando formatação com textwrap no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap no Python: formate textos

    Aprenda textwrap no Python para quebrar, preencher, encurtar, indentar e remover recuos de textos com controle de largura e espaços.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Pasta e lupa representando filtros de nomes com fnmatch no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch no Python: filtre nomes de arquivos

    Aprenda fnmatch no Python para filtrar nomes de arquivos com curingas, controlar maiúsculas, excluir padrões e evitar confundir glob com

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor com dados binários representando arrays numéricos compactos no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    array no Python: números compactos

    Aprenda array no Python para armazenar números compactos, manipular bytes, arquivos binários e buffers com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    Círculo cromático representando conversões RGB, HSV e HLS com colorsys no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys no Python: RGB, HSV e HLS

    Aprenda colorsys no Python para converter cores entre RGB, HSV, HLS e YIQ, gerar paletas e evitar erros com escalas

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Ícone de configuração representando arquivos plist com plistlib no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib no Python: arquivos plist

    Aprenda plistlib no Python para ler e gravar arquivos plist XML e binários, validar dados e integrar configurações Apple com

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026