12.1. pickle
— Serialização de objetos Python¶
Código Fonte: Lib/pickle.py
O módulo pickle
implementa protocolos binários para serializar e desserializar uma estrutura de objeto Python. “Pickling” é o processo pelo qual uma hierarquia de objetos Python é convertida em um fluxo de bytes, e “unpickling” é a operação inversa, em que um fluxo de bytes (de um arquivo binário ou objeto byte ou similar) é convertido de volta em uma hierarquia de objetos. Pickling (e unpickling) é alternativamente conhecido como “serialização”, “marshalling” 1 ou “flattening”; no entanto, para evitar confusão, os termos usados aqui são “pickling” e “unpickling”.
Aviso
The pickle
module is not secure against erroneous or maliciously
constructed data. Never unpickle data received from an untrusted or
unauthenticated source.
12.1.1. Relacionamento com outros módulos Python¶
12.1.1.1. Comparação com marshal
¶
Python tem um módulo de serialização mais primitivo chamado marshal
, mas em geral pickle
deve ser sempre a forma preferida de serializar objetos Python. marshal
existe principalmente para oferecer suporte a arquivos .pyc
do Python.
O módulo pickle
difere do marshal
de várias maneiras significativas:
O módulo
pickle
mantém o controle dos objetos que já serializou, para que referências posteriores ao mesmo objeto não sejam serializadas novamente.marshal
não faz isso.Isso tem implicações tanto para objetos recursivos quanto para compartilhamento de objetos. Objetos recursivos são objetos que contêm referências a si mesmos. Eles não são tratados pelo marshal e, de fato, tentar usar marshal em objetos recursivos irá travar seu interpretador Python. O compartilhamento de objetos ocorre quando há várias referências ao mesmo objeto em locais diferentes na hierarquia de objetos sendo serializados.
pickle
armazena tais objetos apenas uma vez, e garante que todas as outras referências apontem para a cópia mestre. Os objetos compartilhados permanecem compartilhados, o que pode ser muito importante para objetos mutáveis.marshal
não pode ser usado para serializar classes definidas pelo usuário e suas instâncias.pickle
pode salvar e restaurar instâncias de classe de forma transparente, no entanto, a definição de classe deve ser importável e viver no mesmo módulo de quando o objeto foi armazenado.The
marshal
serialization format is not guaranteed to be portable across Python versions. Because its primary job in life is to support.pyc
files, the Python implementers reserve the right to change the serialization format in non-backwards compatible ways should the need arise. Thepickle
serialization format is guaranteed to be backwards compatible across Python releases.
12.1.1.2. Comparação com json
¶
Existem diferenças fundamentais entre os protocolos pickle e JSON (JavaScript Object Notation):
JSON é um formato de serialização de texto (ele produz texto unicode, embora na maioria das vezes seja codificado para
utf-8
), enquanto pickle é um formato de serialização binário;JSON é legível por humanos, enquanto pickle não é;
JSON é interoperável e amplamente usado fora do ecossistema Python, enquanto pickle é específico para Python;
JSON, by default, can only represent a subset of the Python built-in types, and no custom classes; pickle can represent an extremely large number of Python types (many of them automatically, by clever usage of Python’s introspection facilities; complex cases can be tackled by implementing specific object APIs).
Ver também
O módulo json
: um módulo de biblioteca padrão que permite a serialização e desserialização JSON.
12.1.2. Formato de fluxo de dados¶
O formato de dados usado por pickle
é específico do Python. Isso tem a vantagem de não haver restrições impostas por padrões externos, como JSON ou XDR (que não podem representar o compartilhamento de ponteiro); no entanto, isso significa que programas não Python podem não ser capazes de reconstruir objetos Python conservados.
Por padrão, o formato de dados do pickle
usa uma representação binária relativamente compacta. Se você precisa de características de tamanho ideal, pode com eficiência comprimir dados processados com pickle.
O módulo pickletools
contém ferramentas para analisar fluxos de dados gerados por pickle
. O código-fonte do pickletools
tem extensos comentários sobre códigos de operações usados por protocolos de pickle.
There are currently 5 different protocols which can be used for pickling. The higher the protocol used, the more recent the version of Python needed to read the pickle produced.
A versão 0 do protocolo é o protocolo original “legível por humanos” e é compatível com versões anteriores do Python.
A versão 1 do protocolo é um formato binário antigo que também é compatível com versões anteriores do Python.
A versão 2 do protocolo foi introduzida no Python 2.3. Ela fornece uma separação muito mais eficiente de classes estilo novo. Consulte PEP 307 para obter informações sobre as melhorias trazidas pelo protocolo 2.
Protocol version 3 was added in Python 3.0. It has explicit support for
bytes
objects and cannot be unpickled by Python 2.x. This is the default protocol, and the recommended protocol when compatibility with other Python 3 versions is required.Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. Refer to PEP 3154 for information about improvements brought by protocol 4.
Nota
A serialização é uma noção mais primitiva do que a persistência; embora o pickle
leia e escreva objetos de arquivo, ele não lida com a questão de nomear objetos persistentes, nem a questão (ainda mais complicada) de acesso simultâneo a objetos persistentes. O módulo pickle
pode transformar um objeto complexo em um fluxo de bytes e pode transformar o fluxo de bytes em um objeto com a mesma estrutura interna. Talvez a coisa mais óbvia a fazer com esses fluxos de bytes seja escrevê-los em um arquivo, mas também é concebível enviá-los através de uma rede ou armazená-los em um banco de dados. O módulo shelve
fornece uma interface simples para fazer pickle e unpickle de objetos em arquivos de banco de dados no estilo DBM.
12.1.3. Interface do módulo¶
Para serializar uma hierarquia de objeto, você simplesmente chama a função dumps()
. Da mesma forma, para desserializar um fluxo de dados, você chama a função loads()
. No entanto, se você quiser mais controle sobre a serialização e desserialização, pode criar um objeto Pickler
ou Unpickler
, respectivamente.
O módulo pickle
fornece as seguintes constantes:
-
pickle.
HIGHEST_PROTOCOL
¶ Um inteiro, a mais alta versão de protocolo disponível. Este valor pode ser passado como um valor de protocol para as funções
dump()
edumps()
, bem como o construtor dePickler
.
-
pickle.
DEFAULT_PROTOCOL
¶ An integer, the default protocol version used for pickling. May be less than
HIGHEST_PROTOCOL
. Currently the default protocol is 3, a new protocol designed for Python 3.
O módulo pickle
fornece as seguintes funções para tornar o processo de pickling mais conveniente:
-
pickle.
dump
(obj, file, protocol=None, *, fix_imports=True)¶ Write a pickled representation of obj to the open file object file. This is equivalent to
Pickler(file, protocol).dump(obj)
.O argumento opcional protocol, um inteiro, diz ao pickler para usar o protocolo fornecido; os protocolos suportados são de 0 a
HIGHEST_PROTOCOL
. Se não for especificado, o padrão éDEFAULT_PROTOCOL
. Se um número negativo for especificado,HIGHEST_PROTOCOL
é selecionado.O argumento file deve ter um método write() que aceite um argumento de um único byte. Portanto, pode ser um arquivo em disco aberto para escrita binária, uma instância
io.BytesIO
ou qualquer outro objeto personalizado que atenda a esta interface.Se fix_imports for verdadeiro e protocolo for menor que 3, pickle tentará mapear os novos nomes do Python 3 para os nomes dos módulos antigos usados no Python 2, de modo que o fluxo de dados pickle seja legível com o Python 2.
-
pickle.
dumps
(obj, protocol=None, *, fix_imports=True)¶ Return the pickled representation of the object as a
bytes
object, instead of writing it to a file.Arguments protocol and fix_imports have the same meaning as in
dump()
.
-
pickle.
load
(file, *, fix_imports=True, encoding="ASCII", errors="strict")¶ Read a pickled object representation from the open file object file and return the reconstituted object hierarchy specified therein. This is equivalent to
Unpickler(file).load()
.The protocol version of the pickle is detected automatically, so no protocol argument is needed. Bytes past the pickled object’s representation are ignored.
The argument file must have two methods, a read() method that takes an integer argument, and a readline() method that requires no arguments. Both methods should return bytes. Thus file can be an on-disk file opened for binary reading, an
io.BytesIO
object, or any other custom object that meets this interface.Optional keyword arguments are fix_imports, encoding and errors, which are used to control compatibility support for pickle stream generated by Python 2. If fix_imports is true, pickle will try to map the old Python 2 names to the new names used in Python 3. The encoding and errors tell pickle how to decode 8-bit string instances pickled by Python 2; these default to ‘ASCII’ and ‘strict’, respectively. The encoding can be ‘bytes’ to read these 8-bit string instances as bytes objects. Using
encoding='latin1'
is required for unpickling NumPy arrays and instances ofdatetime
,date
andtime
pickled by Python 2.
-
pickle.
loads
(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")¶ Read a pickled object hierarchy from a
bytes
object and return the reconstituted object hierarchy specified therein.The protocol version of the pickle is detected automatically, so no protocol argument is needed. Bytes past the pickled object’s representation are ignored.
Optional keyword arguments are fix_imports, encoding and errors, which are used to control compatibility support for pickle stream generated by Python 2. If fix_imports is true, pickle will try to map the old Python 2 names to the new names used in Python 3. The encoding and errors tell pickle how to decode 8-bit string instances pickled by Python 2; these default to ‘ASCII’ and ‘strict’, respectively. The encoding can be ‘bytes’ to read these 8-bit string instances as bytes objects. Using
encoding='latin1'
is required for unpickling NumPy arrays and instances ofdatetime
,date
andtime
pickled by Python 2.
O módulo pickle
define três exceções:
-
exception
pickle.
PickleError
¶ Classe base comum para as outras exceções de serialização com pickle. Herda
Exception
.
-
exception
pickle.
PicklingError
¶ Erro levantado quando um objeto não serializável com pickle é encontrado por
Pickler
. HerdaPickleError
.Consulte O que pode ser serializado e desserializado com pickle? para saber quais tipos de objetos podem ser serializados com pickle.
-
exception
pickle.
UnpicklingError
¶ Erro levantado quando há um problema ao desserializar com pickle um objeto, como dados corrompidos ou violação de segurança. Herda
PickleError
.Observe que outras exceções também podem ser levantadas durante a desserialização com pickle, incluindo (mas não necessariamente limitado a) AttributeError, EOFError, ImportError e IndexError.
The pickle
module exports two classes, Pickler
and
Unpickler
:
-
class
pickle.
Pickler
(file, protocol=None, *, fix_imports=True)¶ Isso leva um arquivo binário a escrever um fluxo de dados pickle.
O argumento opcional protocol, um inteiro, diz ao pickler para usar o protocolo fornecido; os protocolos suportados são de 0 a
HIGHEST_PROTOCOL
. Se não for especificado, o padrão éDEFAULT_PROTOCOL
. Se um número negativo for especificado,HIGHEST_PROTOCOL
é selecionado.O argumento file deve ter um método write() que aceite um argumento de um único byte. Portanto, pode ser um arquivo em disco aberto para escrita binária, uma instância
io.BytesIO
ou qualquer outro objeto personalizado que atenda a esta interface.Se fix_imports for verdadeiro e protocolo for menor que 3, pickle tentará mapear os novos nomes do Python 3 para os nomes dos módulos antigos usados no Python 2, de modo que o fluxo de dados pickle seja legível com o Python 2.
-
dump
(obj)¶ Write a pickled representation of obj to the open file object given in the constructor.
-
persistent_id
(obj)¶ Não faz nada por padrão. Isso existe para que uma subclasse possa substituí-lo.
Se
persistent_id()
retornarNone
, obj é serializado com pickle como de costume. Qualquer outro valor faz com quePickler
emita o valor retornado como um ID persistente para obj. O significado deste ID persistente deve ser definido porUnpickler.persistent_load()
. Observe que o valor retornado porpersistent_id()
não pode ter um ID persistente.Consulte Persistência de objetos externos para detalhes e exemplos de usos.
-
dispatch_table
¶ A tabela de despacho de um objeto pickler é um registro de funções de redução do tipo que pode ser declarado usando
copyreg.pickle()
. É um mapeamento cujas chaves são classes e cujos valores são funções de redução. Uma função de redução leva um único argumento da classe associada e deve estar de acordo com a mesma interface de um método__reduce__()
.Por padrão, um objeto pickler não terá um atributo
dispatch_table
, e em vez disso usará a tabela de despacho global gerenciada pelo módulocopyreg
. No entanto, para personalizar a serialização com pickle de um objeto pickler específico, pode-se definir o atributodispatch_table
para um objeto do tipo dict. Alternativamente, se uma subclasse dePickler
tem um atributodispatch_table
então ele será usado como a tabela de despacho padrão para instâncias daquela classe.Consulte Tabelas de despacho para exemplos de uso.
Novo na versão 3.3.
-
fast
¶ Descontinuado. Ative o modo rápido se definido como um valor verdadeiro. O modo rápido desabilita o uso de memo, portanto, agilizando o processo de serialização com pickle por não gerar códigos de operação PUT supérfluos. Ele não deve ser usado com objetos autorreferenciais, fazer o contrário fará com que
Pickler
recorra infinitamente.Use
pickletools.optimize()
se você precisar de serializações com pickle mais compactas.
-
-
class
pickle.
Unpickler
(file, *, fix_imports=True, encoding="ASCII", errors="strict")¶ Recebe um arquivo binário para ler um fluxo de dados pickle.
A versão do protocolo do pickle é detectada automaticamente, portanto, nenhum argumento de protocolo é necessário.
The argument file must have two methods, a read() method that takes an integer argument, and a readline() method that requires no arguments. Both methods should return bytes. Thus file can be an on-disk file object opened for binary reading, an
io.BytesIO
object, or any other custom object that meets this interface.Optional keyword arguments are fix_imports, encoding and errors, which are used to control compatibility support for pickle stream generated by Python 2. If fix_imports is true, pickle will try to map the old Python 2 names to the new names used in Python 3. The encoding and errors tell pickle how to decode 8-bit string instances pickled by Python 2; these default to ‘ASCII’ and ‘strict’, respectively. The encoding can be ‘bytes’ to read these 8-bit string instances as bytes objects.
-
load
()¶ Read a pickled object representation from the open file object given in the constructor, and return the reconstituted object hierarchy specified therein. Bytes past the pickled object’s representation are ignored.
-
persistent_load
(pid)¶ Levanta um
UnpicklingError
por padrão.Se definido,
persistent_load()
deve retornar o objeto especificado pelo ID persistente pid. Se um ID persistente inválido for encontrado, umaUnpicklingError
deve ser levantada.Consulte Persistência de objetos externos para detalhes e exemplos de usos.
-
find_class
(module, name)¶ Importa module se necessário e retorna o objeto chamado name dele, onde os argumentos module e name são objetos :classe:`str`. Observe, ao contrário do que seu nome sugere,
find_class()
também é usado para encontrar funções.As subclasses podem substituir isso para obter controle sobre quais tipos de objetos e como eles podem ser carregados, reduzindo potencialmente os riscos de segurança. Confira Restringindo globais para detalhes.
-
12.1.4. O que pode ser serializado e desserializado com pickle?¶
Os seguintes tipos podem ser serializados com pickle:
None
,True
, eFalse
inteiros, números de ponto flutuante, números complexos
strings, bytes, bytearrays
tuplas, listas, conjuntos e dicionários contendo apenas objetos serializáveis com pickle
funções definidas no nível superior de um módulo (usando
def
, nãolambda
)funções embutidas definidas no nível superior de um módulo
classes que são definidas no nível superior de um módulo
instâncias de classes cujo
__dict__
ou o resultado da chamada de__getstate__()
seja serializável com pickle (veja a seção Serializando com pickle instâncias de classes para detalhes).
As tentativas de serializar objetos não serializáveis com pickle vão levantar a exceção PicklingError
; quando isso acontece, um número não especificado de bytes pode já ter sido escrito no arquivo subjacente. Tentar serializar com pickle uma estrutura de dados altamente recursiva pode exceder a profundidade máxima de recursão, a RecursionError
será levantada neste caso. Você pode aumentar este limite cuidadosamente com sys.setrecursionlimit()
.
Observe que as funções (embutidas e definidas pelo usuário) são serializadas com pickle por referência de nome “totalmente qualificado”, não por valor. 2 Isso significa que apenas o nome da função é serializado com pickle, junto com o nome do módulo no qual a função está definida. Nem o código da função, nem qualquer um de seus atributos de função são serializados com pickle. Assim, o módulo de definição deve ser importável no ambiente de desserialização com pickle, e o módulo deve conter o objeto nomeado, caso contrário, uma exceção será levantada. 3
Da mesma forma, as classes são serializadas com pickle por referência nomeada, portanto, aplicam-se as mesmas restrições no ambiente de desserialização com pickle. Observe que nenhum código ou dado da classe é coletado, portanto, no exemplo a seguir, o atributo de classe attr
não é restaurado no ambiente de desserialização com pickle:
class Foo:
attr = 'A class attribute'
picklestring = pickle.dumps(Foo)
Essas restrições são a razão pela qual as funções e classes serializáveis com pickle devem ser definidas no nível superior de um módulo.
Da mesma forma, quando as instâncias da classe são serializadas com pickle, o código e os dados de sua classe não são serializados junto com elas. Apenas os dados da instância são serializados com pickle. Isso é feito propositalmente, para que você possa corrigir bugs em uma classe ou adicionar métodos à classe e ainda carregar objetos que foram criados com uma versão anterior da classe. Se você planeja ter objetos de longa duração que verão muitas versões de uma classe, pode valer a pena colocar um número de versão nos objetos para que as conversões adequadas possam ser feitas pelo método __setstate__()
da classe.
12.1.5. Serializando com pickle instâncias de classes¶
Nesta seção, descrevemos os mecanismos gerais disponíveis para você definir, personalizar e controlar como as instâncias de classe são serializadas e desserializadas com pickle.
Na maioria dos casos, nenhum código adicional é necessário para tornar as instâncias serializáveis com pickle. Por padrão, o pickle recuperará a classe e os atributos de uma instância por meio de introspecção. Quando uma instância de classe não está serializada com pickle, seu método __init__()
geralmente não é invocado. O comportamento padrão primeiro cria uma instância não inicializada e, em seguida, restaura os atributos salvos. O código a seguir mostra uma implementação desse comportamento:
def save(obj):
return (obj.__class__, obj.__dict__)
def load(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
As classes podem alterar o comportamento padrão, fornecendo um ou vários métodos especiais:
-
object.
__getnewargs_ex__
()¶ Nos protocolos 2 e mais recentes, as classes que implementam o método
__getnewargs_ex__()
podem ditar os valores passados para o método__new__()
após a desserialização com pickle. O método deve retornar um par(args, kwargs)
onde args é uma tupla de argumentos posicionais e kwargs um dicionário de argumentos nomeados para construir o objeto. Esses serão passados para o método__new__()
após a desserialização com pickle.Você deve implementar este método se o método
__new__()
de sua classe requer argumentos somente-nomeados. Caso contrário, é recomendado para compatibilidade implementar__getnewargs__()
.Alterado na versão 3.6:
__getnewargs_ex__()
é agora usado em protocolos 2 e 3.
-
object.
__getnewargs__
()¶ Este método serve a um propósito semelhante ao de
__getnewargs_ex__()
, mas tem suporte apenas a argumentos posicionais. Ele deve retornar uma tupla de argumentosargs
que serão passados para o método__new__()
após a desserialização com pickle.__getnewargs__()
não será chamado se__getnewargs_ex__()
estiver definido.Alterado na versão 3.6: Antes do Python 3.6,
__getnewargs__()
era chamado em vez de__getnewargs_ex__()
nos protocolos 2 e 3.
-
object.
__getstate__
()¶ As classes podem influenciar ainda mais como suas instâncias são serializadas com pickle; se a classe define o método
__getstate__()
, ele é chamado e o objeto retornado é serializado com pickle como o conteúdo da instância, ao invés do conteúdo do dicionário da instância. Se o método__getstate__()
estiver ausente, o__dict__
da instância é serializado com pickle como de costume.
-
object.
__setstate__
(state)¶ Ao desserializar com pickle, se a classe define
__setstate__()
, ela é chamada com o estado não desserializado. Nesse caso, não há nenhum requisito para que o objeto de estado seja um dicionário. Caso contrário, o estado serializado com pickle deve ser um dicionário e seus itens são atribuídos ao dicionário da nova instância.Nota
Se
__getstate__()
retornar um valor falso, o método__setstate__()
não será chamado quando da desserialização com pickle.
Confira a seção Manipulação de objetos com estado para mais informações sobre como usar os métodos __getstate__()
e __setstate__()
.
Nota
At unpickling time, some methods like __getattr__()
,
__getattribute__()
, or __setattr__()
may be called upon the
instance. In case those methods rely on some internal invariant being
true, the type should implement __getnewargs__()
or
__getnewargs_ex__()
to establish such an invariant; otherwise,
neither __new__()
nor __init__()
will be called.
Como veremos, o pickle não usa diretamente os métodos descritos acima. Na verdade, esses métodos são parte do protocolo de cópia que implementa o método especial __reduce__()
. O protocolo de cópia fornece uma interface unificada para recuperar os dados necessários para serialização com pickle e cópia de objetos. 4
Apesar de poderoso, implementar __reduce__()
diretamente em sua classe é algo propenso a erro. Por este motivo, designers de classe devem usar a interface de alto nível (ou seja, __getnewargs_ex__()
, __getstate__()
e __setstate__()
) sempre que possível. Vamos mostrar, porém, casos em que o uso de __reduce__()
é a única opção ou leva a uma serialização com pickle mais eficiente, ou as ambas.
-
object.
__reduce__
()¶ A interface está atualmente definida da seguinte maneira. O método
__reduce__()
não aceita nenhum argumento e deve retornar uma string ou preferencialmente uma tupla (o objeto retornado é frequentemente referido como o “valor de redução”).Se uma string é retornada, ela deve ser interpretada como o nome de uma variável global. Deve ser o nome local do objeto relativo ao seu módulo; o módulo pickle pesquisa o espaço de nomes do módulo para determinar o módulo do objeto. Esse comportamento é normalmente útil para singletons.
When a tuple is returned, it must be between two and five items long. Optional items can either be omitted, or
None
can be provided as their value. The semantics of each item are in order:Um objeto chamável que será chamado para criar a versão inicial do objeto.
Uma tupla de argumentos para o objeto chamável. Uma tupla vazia deve ser fornecida se o chamável não aceitar nenhum argumento.
Opcionalmente, o estado do objeto, que será passado para o método
__setstate__()
do objeto conforme descrito anteriormente. Se o objeto não tiver tal método, o valor deve ser um dicionário e será adicionado ao atributo__dict__
do objeto.Opcionalmente, um iterador (e não uma sequência) produzindo itens sucessivos. Esses itens serão anexados ao objeto usando
obj.append(item)
ou, em lote, usandoobj.extend(lista_de_itens)
. Isso é usado principalmente para subclasses de lista, mas pode ser usado por outras classes, desde que tenham os métodosappend()
eextend()
com a assinatura apropriada. (Seappend()
ouextend()
é usado depende de qual versão do protocolo pickle é usada, bem como o número de itens a anexar, então ambos devem ser suportados.)Opcionalmente, um iterador (não uma sequência) produzindo pares de valor-chave sucessivos. Esses itens serão armazenados no objeto usando
obj[chave]=valor
. Isso é usado principalmente para subclasses de dicionário, mas pode ser usado por outras classes, desde que implementem__setitem__()
.
-
object.
__reduce_ex__
(protocol)¶ Alternativamente, um método
__reduce_ex__()
pode ser definido. A única diferença é que este método deve ter um único argumento inteiro, a versão do protocolo. Quando definido, pickle irá preferir isso ao método__reduce__()
. Além disso,__reduce__()
automaticamente se torna um sinônimo para a versão estendida. O principal uso desse método é fornecer valores de redução com compatibilidade reversa para versões mais antigas do Python.
12.1.5.1. Persistência de objetos externos¶
Para o benefício da persistência do objeto, o módulo pickle
tem suporte à noção de uma referência a um objeto fora do fluxo de dados serializados com pickle. Esses objetos são referenciados por um ID persistente, que deve ser uma string de caracteres alfanuméricos (para o protocolo 0) 5 ou apenas um objeto arbitrário (para qualquer protocolo mais recente).
The resolution of such persistent IDs is not defined by the pickle
module; it will delegate this resolution to the user defined methods on the
pickler and unpickler, persistent_id()
and
persistent_load()
respectively.
To pickle objects that have an external persistent id, the pickler must have a
custom persistent_id()
method that takes an object as an
argument and returns either None
or the persistent id for that object.
When None
is returned, the pickler simply pickles the object as normal.
When a persistent ID string is returned, the pickler will pickle that object,
along with a marker so that the unpickler will recognize it as a persistent ID.
Para desserializar com pickle objetos externos, o unpickler deve ter um método persistent_load()
personalizado que recebe um objeto de ID persistente e retorna o objeto referenciado.
Aqui está um exemplo abrangente que apresenta como o ID persistente pode ser usado para serializar com pickle objetos externos por referência.
# Simple example presenting how persistent ID can be used to pickle
# external objects by reference.
import pickle
import sqlite3
from collections import namedtuple
# Simple class representing a record in our database.
MemoRecord = namedtuple("MemoRecord", "key, task")
class DBPickler(pickle.Pickler):
def persistent_id(self, obj):
# Instead of pickling MemoRecord as a regular class instance, we emit a
# persistent ID.
if isinstance(obj, MemoRecord):
# Here, our persistent ID is simply a tuple, containing a tag and a
# key, which refers to a specific record in the database.
return ("MemoRecord", obj.key)
else:
# If obj does not have a persistent ID, return None. This means obj
# needs to be pickled as usual.
return None
class DBUnpickler(pickle.Unpickler):
def __init__(self, file, connection):
super().__init__(file)
self.connection = connection
def persistent_load(self, pid):
# This method is invoked whenever a persistent ID is encountered.
# Here, pid is the tuple returned by DBPickler.
cursor = self.connection.cursor()
type_tag, key_id = pid
if type_tag == "MemoRecord":
# Fetch the referenced record from the database and return it.
cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
key, task = cursor.fetchone()
return MemoRecord(key, task)
else:
# Always raises an error if you cannot return the correct object.
# Otherwise, the unpickler will think None is the object referenced
# by the persistent ID.
raise pickle.UnpicklingError("unsupported persistent object")
def main():
import io
import pprint
# Initialize and populate our database.
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
tasks = (
'give food to fish',
'prepare group meeting',
'fight with a zebra',
)
for task in tasks:
cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))
# Fetch the records to be pickled.
cursor.execute("SELECT * FROM memos")
memos = [MemoRecord(key, task) for key, task in cursor]
# Save the records using our custom DBPickler.
file = io.BytesIO()
DBPickler(file).dump(memos)
print("Pickled records:")
pprint.pprint(memos)
# Update a record, just for good measure.
cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")
# Load the records from the pickle data stream.
file.seek(0)
memos = DBUnpickler(file, conn).load()
print("Unpickled records:")
pprint.pprint(memos)
if __name__ == '__main__':
main()
12.1.5.2. Tabelas de despacho¶
Se alguém quiser personalizar a serialização com pickle de algumas classes sem perturbar nenhum outro código que dependa da serialização, pode-se criar um pickler com uma tabela de despacho privada.
A tabela de despacho global gerenciada pelo módulo copyreg
está disponível como copyreg.dispatch_table
. Portanto, pode-se escolher usar uma cópia modificada de copyreg.dispatch_table
como uma tabela de despacho privada.
Por exemplo
f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = reduce_SomeClass
cria uma instância de pickle.Pickler
com uma tabela de despacho privada que trata a classe SomeClass
especialmente. Alternativamente, o código
class MyPickler(pickle.Pickler):
dispatch_table = copyreg.dispatch_table.copy()
dispatch_table[SomeClass] = reduce_SomeClass
f = io.BytesIO()
p = MyPickler(f)
faz o mesmo, mas todas as instâncias de MyPickler
irão por padrão compartilhar a mesma tabela de despacho. O código equivalente usando o módulo copyreg
é
copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)
12.1.5.3. Manipulação de objetos com estado¶
Aqui está um exemplo que mostra como modificar o comportamento de serialização com pickle de uma classe. A classe TextReader
abre um arquivo texto e retorna o número da linha e o conteúdo da linha cada vez que seu método readline()
é chamado. Se uma instância de TextReader
for selecionada, todos os atributos exceto o membro do objeto arquivo são salvos. Quando a instância é removida, o arquivo é reaberto e a leitura continua a partir do último local. Os métodos __setstate__()
e __getstate__()
são usados para implementar este comportamento.
class TextReader:
"""Print and number lines in a text file."""
def __init__(self, filename):
self.filename = filename
self.file = open(filename)
self.lineno = 0
def readline(self):
self.lineno += 1
line = self.file.readline()
if not line:
return None
if line.endswith('\n'):
line = line[:-1]
return "%i: %s" % (self.lineno, line)
def __getstate__(self):
# Copy the object's state from self.__dict__ which contains
# all our instance attributes. Always use the dict.copy()
# method to avoid modifying the original state.
state = self.__dict__.copy()
# Remove the unpicklable entries.
del state['file']
return state
def __setstate__(self, state):
# Restore instance attributes (i.e., filename and lineno).
self.__dict__.update(state)
# Restore the previously opened file's state. To do so, we need to
# reopen it and read from it until the line count is restored.
file = open(self.filename)
for _ in range(self.lineno):
file.readline()
# Finally, save the file.
self.file = file
Um exemplo de uso pode ser algo assim:
>>> reader = TextReader("hello.txt")
>>> reader.readline()
'1: Hello world!'
>>> reader.readline()
'2: I am line number two.'
>>> new_reader = pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Goodbye!'
12.1.6. Restringindo globais¶
Por padrão, a desserialização com pickle importará qualquer classe ou função que encontrar nos dados pickle. Para muitos aplicativos, esse comportamento é inaceitável, pois permite que o unpickler importe e invoque código arbitrário. Basta considerar o que este fluxo de dados pickle feito à mão faz quando carregado:
>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0
Neste exemplo, o unpickler importa a função os.system()
e então aplica o argumento string “echo hello world”. Embora este exemplo seja inofensivo, não é difícil imaginar um que possa danificar seu sistema.
Por esta razão, você pode querer controlar o que é desserializado com pickle personalizando Unpickler.find_class()
. Ao contrário do que seu nome sugere, Unpickler.find_class()
é chamado sempre que um global (ou seja, uma classe ou uma função) é solicitado. Assim, é possível proibir completamente os globais ou restringi-los a um subconjunto seguro.
Aqui está um exemplo de um unpickler que permite que apenas algumas classes seguras do módulo builtins
sejam carregadas:
import builtins
import io
import pickle
safe_builtins = {
'range',
'complex',
'set',
'frozenset',
'slice',
}
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
# Only allow safe classes from builtins.
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
# Forbid everything else.
raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
(module, name))
def restricted_loads(s):
"""Helper function analogous to pickle.loads()."""
return RestrictedUnpickler(io.BytesIO(s)).load()
Um exemplo de uso do nosso unpickler funcionando como esperado:
>>> restricted_loads(pickle.dumps([1, 2, range(15)]))
[1, 2, range(0, 15)]
>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback (most recent call last):
...
pickle.UnpicklingError: global 'os.system' is forbidden
>>> restricted_loads(b'cbuiltins\neval\n'
... b'(S\'getattr(__import__("os"), "system")'
... b'("echo hello world")\'\ntR.')
Traceback (most recent call last):
...
pickle.UnpicklingError: global 'builtins.eval' is forbidden
Como nossos exemplos mostram, você deve ter cuidado com o que permite que seja desserializado com pickle. Portanto, se a segurança é uma preocupação, você pode querer considerar alternativas como a API de marshalling em xmlrpc.client
ou soluções de terceiros.
12.1.7. Performance¶
Versões recentes do protocolo pickle (do protocolo 2 em diante) apresentam codificações binárias eficientes para vários recursos comuns e tipos embutidos. Além disso, o módulo pickle
tem um otimizador transparente escrito em C.
12.1.8. Exemplos¶
Para código mais simples, use as funções dump()
e load()
.
import pickle
# An arbitrary collection of objects supported by pickle.
data = {
'a': [1, 2.0, 3, 4+6j],
'b': ("character string", b"byte string"),
'c': {None, True, False}
}
with open('data.pickle', 'wb') as f:
# Pickle the 'data' dictionary using the highest protocol available.
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
O exemplo a seguir lê os dados resultantes em serializados com pickle.
import pickle
with open('data.pickle', 'rb') as f:
# The protocol version used is detected automatically, so we do not
# have to specify it.
data = pickle.load(f)
Ver também
- Módulo
copyreg
Registro de construtor de interface Pickle para tipos de extensão.
- Módulo
pickletools
Ferramentas para trabalhar e analisar dados serializados com pickle.
- Módulo
shelve
Banco de dados indexado de objetos; usa
pickle
.- Módulo
copy
Cópia rasa e cópia profunda de objeto.
- Módulo
marshal
Serialização de alto desempenho de tipos embutidos.
Notas de rodapé
- 1
Não confunda isso com o módulo
marshal
- 2
This is why
lambda
functions cannot be pickled: alllambda
functions share the same name:<lambda>
.- 3
A exceção levantada provavelmente será uma
ImportError
ou umaAttributeError
, mas poderia ser outra coisa.- 4
O módulo
copy
usa este protocolo para operações de cópia rasa e cópia profunda.- 5
A limitação de caracteres alfanuméricos se deve ao fato dos IDs persistentes, no protocolo 0, serem delimitados pelo caractere de nova linha. Portanto, se qualquer tipo de caractere de nova linha ocorrer em IDs persistentes, o pickle resultante se tornará ilegível.