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

    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    functools.partial no Python: guia prático

    Aprenda functools.partial no Python para fixar argumentos, adaptar callbacks e criar funções especializadas com clareza.

    Ler mais

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

    filecmp no Python: compare arquivos e pastas

    Aprenda filecmp no Python para comparar arquivos e pastas, usar shallow, dircmp, cmpfiles, cache e hashes de integridade.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026
    Terminal de comandos representando parsing seguro com shlex no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    shlex no Python: comandos e argumentos seguros

    Aprenda shlex no Python para separar comandos, tratar aspas, usar quote e join e reduzir riscos de injeção ao executar

    Ler mais

    Tempo de leitura: 7 minutos
    02/08/2026
    Banco de dados local representando persistência com shelve no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    shelve no Python: persistência simples

    Aprenda shelve no Python para persistir objetos, atualizar dados mutáveis, evitar riscos de pickle e saber quando migrar para SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    01/08/2026
    Documentos de texto representando comparação de versões com difflib no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    difflib no Python: compare textos e arquivos

    Aprenda difflib no Python para comparar textos, medir similaridade, criar diffs unificados, relatórios HTML e sugestões de nomes.

    Ler mais

    Tempo de leitura: 7 minutos
    01/08/2026
    Painel de gráficos representando análise estatística de dados no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    statistics no Python: análise de dados

    Aprenda statistics no Python para média, mediana, desvio padrão, quantis, correlação, regressão, NormalDist e KDE.

    Ler mais

    Tempo de leitura: 8 minutos
    31/07/2026