Salvar e carregar objetos e usar picles

114

Estou tentando salvar e carregar objetos usando o picklemódulo.
Primeiro eu declaro meus objetos:

>>> class Fruits:pass
...
>>> banana = Fruits()

>>> banana.color = 'yellow'
>>> banana.value = 30

Depois disso, abro um arquivo chamado 'Fruits.obj' (anteriormente, criei um novo arquivo .txt e renomeei 'Fruits.obj'):

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)

Depois disso fecho minha sessão e inicio uma nova e coloco a seguinte (tentando acessar o objeto que deveria ser salvo):

file = open("Fruits.obj",'r')
object_file = pickle.load(file)

Mas eu tenho esta mensagem:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
ValueError: read() from the underlying stream did notreturn bytes

Não sei o que fazer porque não entendo esta mensagem. Alguém sabe como posso carregar meu objeto 'banana'? Obrigado!

EDIT: Como alguns de vocês sugeriram eu coloco:

>>> import pickle
>>> file = open("Fruits.obj",'rb')

Não houve problema, mas o próximo que coloquei foi:

>>> object_file = pickle.load(file)

E eu tenho erro:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
EOFError
Peterstone
fonte

Respostas:

74

Quanto ao seu segundo problema:

 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "C:\Python31\lib\pickle.py", line
 1365, in load encoding=encoding,
 errors=errors).load() EOFError

Depois de ler o conteúdo do arquivo, o ponteiro do arquivo estará no final do arquivo - não haverá mais dados para ler. Você deve retroceder o arquivo para que seja lido desde o início novamente:

file.seek(0)

O que você geralmente deseja fazer, porém, é usar um gerenciador de contexto para abrir o arquivo e ler os dados dele. Dessa forma, o arquivo será fechado automaticamente após a execução do bloco, o que também o ajudará a organizar as operações do arquivo em partes significativas.

Finalmente, cPickle é uma implementação mais rápida do módulo pickle em C. Então:

In [1]: import cPickle

In [2]: d = {"a": 1, "b": 2}

In [4]: with open(r"someobject.pickle", "wb") as output_file:
   ...:     cPickle.dump(d, output_file)
   ...:

# pickle_file will be closed at this point, preventing your from accessing it any further

In [5]: with open(r"someobject.pickle", "rb") as input_file:
   ...:     e = cPickle.load(input_file)
   ...:

In [7]: print e
------> print(e)
{'a': 1, 'b': 2}
Jim Brissom
fonte
Que tipo de estrutura de dados é 'd = {"a": 1, "b": 2}'?
Peterstone
1
@Peterstone: {"a": 1, "b": 2}cria um dicionário com as chaves "a"e "b"nele. Isso é chamado de expressão de exibição de dicionário na documentação online. É apenas uma das várias maneiras diferentes de dictconstruir um objeto do tipo , que é um dos vários tipos de dados internos padrão disponíveis no Python.
martineau
2
Por que a letra 'r' vem depois do nome do arquivo? Não vejo isso nos documentos. Além disso, torna difícil usar uma variável para o nome do arquivo.
SherylHohman
7
Olhando para esta resposta hoje e percebendo que ela se aplica apenas ao Python 2.x. No Python 3.x, deve-se usar diretamente pickleque irá importar cpickleautomaticamente se puder. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Eskapp
41

O seguinte funciona para mim:

class Fruits: pass

banana = Fruits()

banana.color = 'yellow'
banana.value = 30

import pickle

filehandler = open("Fruits.obj","wb")
pickle.dump(banana,filehandler)
filehandler.close()

file = open("Fruits.obj",'rb')
object_file = pickle.load(file)
file.close()

print(object_file.color, object_file.value, sep=', ')
# yellow, 30
martineau
fonte
Isso funciona comigo, mas o que busco é fechar uma sessão, abrir uma nova e carregar o que salvei em uma sessão anterior. Fecho a sessão após colocar a linha "filehandler.close ()" e abro uma nova e coloco o resto do seu código, em seguida, após colocar "object_file = pickle.load (file)", recebo este erro: Traceback ( última chamada mais recente): Arquivo "<pyshell # 5>", linha 1, em <module> object_file = pickle.load (arquivo) Arquivo "C: \ Python31 \ lib \ pickle.py", linha 1365, na codificação de carregamento = codificação, erros = erros) .load () AttributeError: o objeto 'módulo' não tem atributo 'Frutas'
Peterstone
3
@Peterstone: Na segunda sessão você precisará ter uma definição de class Fruitsdefinido para que pickle.load()possa reconstituir o objeto a partir dos dados que foram salvos no arquivo binário. A melhor prática para esse tipo de coisa é colocar a class Fruitsdefinição em um arquivo .py separado (tornando-o um módulo personalizado) e, em seguida, importesse módulo ou itens dele sempre que necessário (ou seja, ambas as sessões). Por exemplo, se você colocá-lo em um arquivo chamado MyDataDefs.py, poderá escrever from MyDataDefs import Fruits. Avise-me se não estiver claro e atualizarei minha resposta de acordo.
martineau de
Na verdade, o PEP 8 recomenda usar todos os caracteres minúsculos para os nomes dos módulos, então o exemplo no final do meu último comentário deveria estar em um arquivo chamado my_data_defs.pyusing from my_data_defs import Fruits.
martineau de
24

Você está se esquecendo de ler como binário também.

Em sua parte de escrita, você tem:

open(b"Fruits.obj","wb") # Note the wb part (Write Binary)

Na parte de leitura você tem:

file = open("Fruits.obj",'r') # Note the r part, there should be a b too

Portanto, substitua-o por:

file = open("Fruits.obj",'rb')

E vai funcionar :)


Quanto ao seu segundo erro, é mais provável que seja causado por não fechar / sincronizar o arquivo corretamente.

Experimente este código para escrever:

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)
>>> filehandler.close()

E isso (inalterado) para ler:

>>> import pickle
>>> file = open("Fruits.obj",'rb')
>>> object_file = pickle.load(file)

Uma versão mais limpa estaria usando a withdeclaração.

Para escrever:

>>> import pickle
>>> with open('Fruits.obj', 'wb') as fp:
>>>     pickle.dump(banana, fp)

Para ler:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)
Wolph
fonte
1
Eu uso a sua versão que usa a instrução with e obtenho esta mensagem: Traceback (última chamada mais recente): Arquivo "<pyshell # 20>", linha 1, em <module> print (banana.color) AttributeError: 'Fruits' objeto não tem atributo de 'cor'
Peterstone
17

Sempre aberto no modo binário, neste caso

file = open("Fruits.obj",'rb')
ismail
fonte
6

Você não abriu o arquivo no modo binário.

open("Fruits.obj",'rb')

Deveria trabalhar.

Para o seu segundo erro, o arquivo provavelmente está vazio, o que significa que você o esvaziou inadvertidamente ou usou o nome de arquivo errado ou algo assim.

(Isso pressupõe que você realmente fechou sua sessão. Caso contrário, é porque você não fechou o arquivo entre a gravação e a leitura).

Testei seu código e funciona.

Lennart Regebro
fonte
3

Parece que você deseja salvar suas instâncias de classe entre as sessões e usar pickleé uma maneira decente de fazer isso. No entanto, há um pacote chamado kleptoque abstrai o salvamento de objetos em uma interface de dicionário, então você pode escolher separar objetos e salvá-los em um arquivo (como mostrado abaixo), ou separar os objetos e salvá-los em um banco de dados, ou em vez de use pickle use json ou muitas outras opções. O bom kleptodisso é que, abstraindo para uma interface comum, torna-se mais fácil para que você não precise se lembrar dos detalhes de baixo nível de como salvar por meio da separação em um arquivo ou de outra forma.

Observe que funciona para atributos de classe adicionados dinamicamente, que o pickle não pode fazer ...

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[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.
>>> from klepto.archives import file_archive 
>>> db = file_archive('fruits.txt')
>>> class Fruits: pass
... 
>>> banana = Fruits()
>>> banana.color = 'yellow'
>>> banana.value = 30
>>> 
>>> db['banana'] = banana 
>>> db.dump()
>>> 

Então nós recomeçamos ...

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[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.
>>> from klepto.archives import file_archive
>>> db = file_archive('fruits.txt')
>>> db.load()
>>> 
>>> db['banana'].color
'yellow'
>>> 

Klepto funciona em python2 e python3.

Obtenha o código aqui: https://github.com/uqfoundation

Mike McKerns
fonte
1

Você pode usar qualquer cache para fazer o trabalho por você. Supondo que você tenha uma função myfuncque cria a instância:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc()
    banana = Fruits()
    banana.color = 'yellow'
    banana.value = 30
return banana

Anycache chama myfuncna primeira vez e seleciona o resultado para um arquivo cachedirusando um identificador exclusivo (dependendo do nome da função e dos argumentos) como nome de arquivo. Em qualquer execução consecutiva, o objeto conservado é carregado.

Se o cachedirfor preservado entre as execuções do Python, o objeto em conserva será obtido da execução anterior do Python.

Os argumentos da função também são levados em consideração. Uma implementação refatorada funciona da mesma forma:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc(color, value)
    fruit = Fruits()
    fruit.color = color
    fruit.value = value
return fruit
c0fec0de
fonte