Eu criei um objeto como este:
company1.name = 'banana'
company1.value = 40
Eu gostaria de salvar este objeto. Como eu posso fazer isso?
protocol=pickle.HIGHEST_PROTOCOL
. Minha resposta também oferece alternativas para picles.
Eu criei um objeto como este:
company1.name = 'banana'
company1.value = 40
Eu gostaria de salvar este objeto. Como eu posso fazer isso?
protocol=pickle.HIGHEST_PROTOCOL
. Minha resposta também oferece alternativas para picles.
Respostas:
Você pode usar o pickle
módulo na biblioteca padrão. Aqui está uma aplicação elementar para o seu exemplo:
import pickle
class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value
with open('company_data.pkl', 'wb') as output:
company1 = Company('banana', 40)
pickle.dump(company1, output, pickle.HIGHEST_PROTOCOL)
company2 = Company('spam', 42)
pickle.dump(company2, output, pickle.HIGHEST_PROTOCOL)
del company1
del company2
with open('company_data.pkl', 'rb') as input:
company1 = pickle.load(input)
print(company1.name) # -> banana
print(company1.value) # -> 40
company2 = pickle.load(input)
print(company2.name) # -> spam
print(company2.value) # -> 42
Você também pode definir seu próprio utilitário simples, como o seguinte, que abre um arquivo e grava um único objeto nele:
def save_object(obj, filename):
with open(filename, 'wb') as output: # Overwrites any existing file.
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
# sample usage
save_object(company1, 'company1.pkl')
Como essa é uma resposta tão popular, gostaria de abordar alguns tópicos de uso um pouco avançados.
cPickle
(ou _pickle
) vspickle
É quase sempre preferível usar o cPickle
módulo, em vez de pickle
porque o primeiro é escrito em C e é muito mais rápido. Existem algumas diferenças sutis entre eles, mas na maioria das situações eles são equivalentes e a versão C fornecerá um desempenho muito superior. Mudar para ele não poderia ser mais fácil, basta alterar a import
declaração para isso:
import cPickle as pickle
No Python 3, cPickle
foi renomeado _pickle
, mas isso não é mais necessário, já que o pickle
módulo agora o faz automaticamente - consulte Que diferença entre pickle e _pickle no python 3? .
O resumo é que você pode usar algo como o seguinte para garantir que seu código sempre use a versão C quando estiver disponível no Python 2 e 3:
try:
import cPickle as pickle
except ModuleNotFoundError:
import pickle
pickle
pode ler e gravar arquivos em vários formatos diferentes, específicos para Python, chamados protocolos, conforme descrito na documentação , "Protocolo versão 0" é ASCII e, portanto, "legível por humanos". As versões> 0 são binárias e a mais alta disponível depende de qual versão do Python está sendo usada. O padrão também depende da versão do Python. No Python 2, o padrão era a versão Protocol 0
, mas no Python 3.8.1, é a versão Protocol 4
. No Python 3.x, o módulo foi pickle.DEFAULT_PROTOCOL
adicionado, mas isso não existe no Python 2.
Felizmente, existe uma abreviação para escrever pickle.HIGHEST_PROTOCOL
em todas as chamadas (assumindo que é isso que você deseja e costuma fazer), basta usar o número literal -1
- semelhante a referenciar o último elemento de uma sequência por meio de um índice negativo. Então, em vez de escrever:
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
Você pode simplesmente escrever:
pickle.dump(obj, output, -1)
De qualquer forma, você só especificaria o protocolo uma vez se tivesse criado um Pickler
objeto para uso em várias operações de pickle:
pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
etc...
Nota : Se você estiver em um ambiente executando versões diferentes do Python, provavelmente desejará usar explicitamente (por exemplo, código rígido) um número de protocolo específico que todos possam ler (versões posteriores geralmente podem ler arquivos produzidos por versões anteriores) .
Enquanto um arquivo de picles pode conter qualquer número de objetos em conserva, como mostrado nas amostras acima, quando há um número desconhecido deles, muitas vezes é mais fácil de armazenar todos eles em algum tipo de recipiente de tamanho variável, como um list
, tuple
ou dict
e escrever todos eles para o arquivo em uma única chamada:
tech_companies = [
Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')
e restaure a lista e tudo mais tarde com:
with open('tech_companies.pkl', 'rb') as input:
tech_companies = pickle.load(input)
A principal vantagem é que você não precisa saber quantas instâncias de objetos são salvas para carregá-las novamente mais tarde (embora fazer isso sem essas informações seja possível, isso requer algum código um pouco especializado). Veja as respostas para a pergunta relacionada Salvando e carregando vários objetos no arquivo pickle? para obter detalhes sobre diferentes maneiras de fazer isso. Pessoalmente, eu gosto mais da resposta de @Lutz Prechelt . Aqui está adaptado aos exemplos aqui:
class Company:
def __init__(self, name, value):
self.name = name
self.value = value
def pickled_items(filename):
""" Unpickle a file of pickled data. """
with open(filename, "rb") as f:
while True:
try:
yield pickle.load(f)
except EOFError:
break
print('Companies in pickle file:')
for company in pickled_items('company_data.pkl'):
print(' name: {}, value: {}'.format(company.name, company.value))
company1
e company2
. Por que você também não exclui Company
e mostra o que acontece?
Eu acho que é uma suposição bastante forte supor que o objeto é a class
. E se não for um class
? Há também a suposição de que o objeto não foi definido no intérprete. E se foi definido no intérprete? Além disso, e se os atributos fossem adicionados dinamicamente? Quando alguns objetos python têm atributos adicionados aos seus __dict__
após a criação, pickle
não respeita a adição desses atributos (ou seja, 'esquece' eles foram adicionados - porque pickle
serializa por referência à definição do objeto).
Em todos esses casos, pickle
e cPickle
pode falhar terrivelmente.
Se você deseja salvar um object
(criado arbitrariamente), onde possui atributos (adicionados na definição do objeto ou posteriormente) ... sua melhor aposta é usar dill
, que pode serializar quase tudo em python.
Começamos com uma aula…
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> with open('company.pkl', 'wb') as f:
... pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
...
>>>
Agora desligue e reinicie ...
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> with open('company.pkl', 'rb') as f:
... company1 = pickle.load(f)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
klass = self.find_class(module, name)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'Company'
>>>
Opa ... pickle
não aguento. Vamos tentar dill
. Vamos lançar outro tipo de objeto (a lambda
) para uma boa medida.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>>
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>>
>>> with open('company_dill.pkl', 'wb') as f:
... dill.dump(company1, f)
... dill.dump(company2, f)
...
>>>
E agora leia o arquivo.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('company_dill.pkl', 'rb') as f:
... company1 = dill.load(f)
... company2 = dill.load(f)
...
>>> company1
<__main__.Company instance at 0x107909128>
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>>
Funciona. A razão pickle
falha, e dill
não, é que dill
trata __main__
como um módulo (na maior parte), e também pode selecionar definições de classe em vez de selecionar por referência (como pickle
faz). O motivo pelo qual dill
um pickle lambda
é dado é que ele dá um nome ... então a magia do pickling pode acontecer.
Na verdade, há uma maneira mais fácil de salvar todos esses objetos, especialmente se você tiver muitos objetos criados. Apenas despeje a sessão inteira do python e volte mais tarde.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>>
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>>
>>> dill.dump_session('dill.pkl')
>>>
Agora desligue o computador, desfrute de um café expresso ou o que quer que seja e volte mais tarde ...
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>
A única grande desvantagem é que dill
não faz parte da biblioteca padrão do python. Portanto, se você não pode instalar um pacote python no seu servidor, não pode usá-lo.
No entanto, se você conseguir instalar pacotes python no seu sistema, poderá obter as informações mais recentes dill
com git+https://github.com/uqfoundation/dill.git@master#egg=dill
. E você pode obter a versão mais recente lançada com pip install dill
.
TypeError: __new__() takes at least 2 arguments (1 given)
ao tentar usar dill
(o que parece promissor) com um objeto bastante complexo que inclui um arquivo de áudio.
TypeError
quando faz o que, exatamente? Isso geralmente é um sinal de ter o número errado de argumentos ao instanciar uma instância de classe. Se isso não fizer parte do fluxo de trabalho da pergunta acima, você poderá publicá-la como outra pergunta, enviá-la por e-mail ou adicioná-la como um problema na dill
página do github?
dill
problema.
dil
Eu me dou MemoryError
embora! o mesmo acontece cPickle
, pickle
e hickle
.
Você pode usar o anycache para fazer o trabalho por você. Ele considera todos os detalhes:
pickle
módulo python para lidar com lambda
todos os recursos legais do python.Supondo que você tenha uma função myfunc
que cria a instância:
from anycache import anycache
class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value
@anycache(cachedir='/path/to/your/cache')
def myfunc(name, value)
return Company(name, value)
O Anycache chama myfunc
na primeira vez e seleciona o resultado em um arquivo cachedir
usando um identificador exclusivo (dependendo do nome da função e de seus argumentos) como nome do arquivo. Em qualquer execução consecutiva, o objeto em conserva é carregado. Se o cachedir
for preservado entre execuções python, o objeto pickled será obtido da execução python anterior.
Para mais detalhes, consulte a documentação
anycache
para salvar mais de uma instância de, digamos, um class
ou contêiner como um list
(que não foi o resultado da chamada de uma função)?
Exemplo rápido usando company1
da sua pergunta, com python3.
import pickle
# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))
# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))
No entanto, como essa resposta observou, o pickle geralmente falha. Então você realmente deve usar dill
.
import dill
# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))
# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))