Zapisywanie i ładowanie przedmiotów oraz używanie marynaty

114

Próbuję zapisywać i ładować obiekty za pomocą picklemodułu.
Najpierw deklaruję moje obiekty:

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

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

Następnie otwieram plik o nazwie „Fruits.obj” (wcześniej utworzyłem nowy plik .txt i zmieniłem nazwę „Fruits.obj”):

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

Po wykonaniu tej czynności zamykam sesję i rozpoczynam nową i stawiam następną (próbując uzyskać dostęp do obiektu, który ma być zapisany):

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

Ale mam taką wiadomość:

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

Nie wiem, co robić, ponieważ nie rozumiem tej wiadomości. Czy ktoś wie, jak mogę załadować mój obiekt „banan”? Dziękuję Ci!

EDYCJA: Jak niektórzy z was sugerowali, umieściłem:

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

Nie było problemu, ale następny włożyłem to:

>>> object_file = pickle.load(file)

I mam błąd:

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
źródło

Odpowiedzi:

74

A co do drugiego problemu:

 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

Po przeczytaniu zawartości pliku wskaźnik pliku znajdzie się na końcu pliku - nie będzie już żadnych danych do odczytania. Musisz przewinąć plik do tyłu, aby został ponownie odczytany od początku:

file.seek(0)

Jednak zazwyczaj chcesz użyć menedżera kontekstu, aby otworzyć plik i odczytać z niego dane. W ten sposób plik zostanie automatycznie zamknięty po zakończeniu wykonywania bloku, co pomoże również zorganizować operacje na plikach w znaczące fragmenty.

Wreszcie, cPickle to szybsza implementacja modułu pikle w C. Więc:

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
źródło
Jaki rodzaj struktury danych to „d = {" a ": 1," b ": 2} '?
Peterstone,
1
@Peterstone: {"a": 1, "b": 2}tworzy słownik z kluczami "a"i "b"w nim. W dokumentacji online nazywa się to wyrażeniem wyświetlanym w słowniku . To tylko jeden z kilku różnych sposobów dictkonstruowania obiektu typu , który jest jednym z kilku standardowych wbudowanych typów danych dostępnych w Pythonie.
martineau
2
Dlaczego litera „r” poprzedza nazwę pliku? Nie widzę tego w dokumentach. Utrudnia również użycie zmiennej w nazwie pliku.
SherylHohman
7
Patrząc dzisiaj na tę odpowiedź i zauważając, że dotyczy ona tylko Pythona 2.x. W Pythonie 3.x należy bezpośrednio użyć pickletego, który importuje cpickleautomatycznie, jeśli jest to możliwe. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Eskapp
41

U mnie działa:

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
źródło
To działa, ale dążę do zamknięcia sesji, otwarcia nowej i załadowania tego, co zapisałem w poprzedniej sesji. Zamykam sesję po wstawieniu linii "filehandler.close ()" i otwieram nową i umieszczam resztę twojego kodu, a następnie po wstawieniu "object_file = pickle.load (file)" pojawia się ten błąd: Traceback ( ostatnie połączenie ostatnie): Plik „<pyshell # 5>”, wiersz 1, w <module> object_file = pickle.load (plik) Plik „C: \ Python31 \ lib \ pickle.py”, wiersz 1365, w kodowaniu ładowania = kodowanie, błędy = błędy) .load () AttributeError: obiekt „moduł” nie ma atrybutu „Owoce”
Peterstone
3
@Peterstone: W drugiej sesji musisz mieć zdefiniowaną class Fruitsdefinicję, aby pickle.load()móc odtworzyć obiekt z danych zapisanych w pliku binarnym. Najlepszą praktyką w tego typu sytuacjach jest umieszczenie class Fruitsdefinicji w oddzielnym pliku .py (czyniąc go niestandardowym modułem), a następnie importtego modułu lub elementów z niego w razie potrzeby (tj. W obu sesjach). Na przykład, jeśli umieścisz go w pliku o nazwie, MyDataDefs.pymożesz pisać from MyDataDefs import Fruits. Daj mi znać, jeśli jest to niejasne, a ja odpowiednio zaktualizuję moją odpowiedź.
martineau
Właściwie PEP 8 zaleca używanie małych liter w nazwach modułów, więc przykład na końcu mojego ostatniego komentarza powinien znajdować się w pliku o nazwie my_data_defs.pyusing from my_data_defs import Fruits.
martineau
24

Zapominasz również czytać to jako binarne.

W swojej części pisemnej masz:

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

W części do czytania masz:

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

Więc zamień go na:

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

I zadziała :)


Jeśli chodzi o drugi błąd, najprawdopodobniej jest to spowodowane nieprawidłowym zamknięciem / synchronizacją pliku.

Spróbuj napisać ten fragment kodu:

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

A to (bez zmian) do przeczytania:

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

Bardziej zgrabna wersja użyłaby tego withoświadczenia.

Do pisania:

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

Do czytania:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)
Wolph
źródło
1
Używam Twojej wersji używającej instrukcji with i otrzymuję następujący komunikat: Traceback (ostatnie połączenie): Plik "<pyshell # 20>", wiersz 1, w <module> print (banana.color) AttributeError: 'Fruits' obiekt nie ma atrybutu „kolor”
Peterstone,
17

W tym przypadku zawsze otwieraj w trybie binarnym

file = open("Fruits.obj",'rb')
ismail
źródło
6

Nie otworzyłeś pliku w trybie binarnym.

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

Powinno działać.

W przypadku drugiego błędu plik jest najprawdopodobniej pusty, co oznacza, że ​​nieumyślnie go opróżniłeś lub użyłeś złej nazwy pliku lub coś w tym stylu.

(Zakładamy, że naprawdę zamknąłeś sesję. Jeśli nie, to dlatego, że nie zamknąłeś pliku między zapisem a odczytem).

Przetestowałem Twój kod i działa.

Lennart Regebro
źródło
3

Wygląda na to, że chcesz zapisywać swoje instancje klas między sesjami, a używanie pickleto przyzwoity sposób na zrobienie tego. Istnieje jednak pakiet o nazwie, kleptoktóry abstrahuje zapisywanie obiektów w interfejsie słownika, więc możesz wybrać marynowanie obiektów i zapisywanie ich do pliku (jak pokazano poniżej) lub marynowanie obiektów i zapisywanie ich w bazie danych lub zamiast użyj pickle użyj json lub wielu innych opcji. Zaletą kleptojest to, że abstrakcja do wspólnego interfejsu ułatwia to, dzięki czemu nie musisz pamiętać niskopoziomowych szczegółów, jak zapisać przez wytrawianie do pliku lub w inny sposób.

Zauważ, że działa to dla dynamicznie dodawanych atrybutów klas, których pikle nie mogą zrobić ...

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()
>>> 

Następnie uruchamiamy ponownie…

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 działa na python2 i python3.

Pobierz kod tutaj: https://github.com/uqfoundation

Mike McKerns
źródło
1

Możesz użyć anycache, aby wykonać pracę za Ciebie. Zakładając, że masz funkcję, myfuncktóra tworzy instancję:

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 wywołuje myfuncpo raz pierwszy i zapisuje wynik do pliku cachedirprzy użyciu unikalnego identyfikatora (w zależności od nazwy funkcji i argumentów) jako nazwy pliku. W każdym kolejnym przebiegu marynowany przedmiot jest ładowany.

Jeśli cachedirzachowany jest między uruchomieniami języka Python, piklowany obiekt jest pobierany z poprzedniego uruchomienia języka Python.

Uwzględniane są również argumenty funkcji. Wdrożenie refaktoryzowane działa podobnie:

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
źródło