Zapisywanie obiektu (trwałość danych)
Stworzyłem taki obiekt:
company1.name = 'banana'
company1.value = 40
Chciałbym zapisać ten obiekt. Jak mogę to zrobić?
3 answers
Możesz użyć modułu pickle
w bibliotece standardowej.
Oto elementarne zastosowanie go do twojego przykładu:
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
Można również napisać proste narzędzie, takie jak następujące, które otwiera plik i zapisuje do niego pojedynczy obiekt:
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')
Aktualizacja:
Ponieważ jest to tak popularna odpowiedź, chciałbym poruszyć kilka nieco zaawansowanych tematów użytkowania.
cPickle
(lub _pickle
) vs pickle
Prawie zawsze lepiej jest użyć cPickle
moduł zamiast pickle
, ponieważ pierwszy jest napisany w C i jest znacznie szybszy. Istnieją pewne subtelne różnice między nimi, ale w większości sytuacji są one równoważne i wersja C zapewni znacznie lepszą wydajność. Po prostu zmień zdanie import
na to:
import cPickle as pickle
W Pythonie 3, cPickle
zmieniono nazwę na _pickle
, ale nie jest to już konieczne, ponieważ moduł pickle
robi to teraz automatycznie-zobacz co? różnica między pickle i _pickle w Pythonie 3?.
try:
import cPickle as pickle
except ModuleNotFoundError:
import pickle
Formaty strumienia danych (protokoły)
pickle
potrafi odczytywać i zapisywać pliki w kilku różnych, specyficznych dla Pythona formatach, zwanych protokołami . "Protokół w wersji 0" jest ASCII, a więc "czytelny dla człowieka". Wersje > 1 są binarne, a najwyższa dostępna zależy od używanej wersji Pythona. Wartość domyślna zależy również od wersji Pythona. W Pythonie 2 domyślną wersją była wersja protokołu 0
, ale w Pythonie 3.6 jest to wersja protokołu 3
. W Pythonie 3.x moduł miał dodaną pickle.DEFAULT_PROTOCOL
, ale nie istnieje ona w Pythonie 2.
Na szczęście w każdym wywołaniu jest skrót do pisania pickle.HIGHEST_PROTOCOL
(zakładając, że tego chcesz i zwykle to robisz) - po prostu użyj literalnej liczby -1
.
Więc zamiast tego o pisaniu:
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
Możesz po prostu napisać:
pickle.dump(obj, output, -1)
Tak czy siak, musiałbyś podać protokół tylko raz, jeśli utworzyłbyś Pickler
obiekt do użycia w wielu operacjach typu pickle:
pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
etc...
Wiele Obiektów
Podczas gdy plik picklemoże zawierać dowolną liczbę marynowanych obiektów, jak pokazano w powyższych przykładach, gdy jest ich nieznana liczba, często łatwiej jest przechowywać je wszystkie w jakimś pojemniku o różnej wielkości, jak np. list
, tuple
, lub dict
i zapisz je wszystkie do pliku jednym wywołaniem:
tech_companies = [
Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')
I przywrócić listę i wszystko w niej później z:
with open('tech_companies.pkl', 'rb') as input:
tech_companies = pickle.load(input)
Główną zaletą jest to, że nie trzeba wiedzieć, ile instancji obiektów jest zapisanych, aby załadować je później (chociaż robienie tego bez tej informacji jest możliwe, wymaga to nieco wyspecjalizowanego kodu). Zobacz odpowiedzi na powiązane pytanie Zapisywanie i ładowanie wielu obiektów w pliku pickle? aby uzyskać szczegóły na różne sposoby na to. Osobiście i jak @ Lutz Prechelt ' S odpowiedź najlepsza. Tutaj jest to dostosowane do przykładów tutaj:
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))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-21 12:27:19
Myślę, że całkiem mocnym założeniem jest założenie, że obiekt jest class
. A jeśli to nie class
? Istnieje również założenie, że obiekt nie został zdefiniowany w interpreterze. Co jeśli zostało zdefiniowane w interpreterze? Co by było, gdyby atrybuty były dodawane dynamicznie? Gdy niektóre obiekty Pythona mają atrybuty dodane do ich __dict__
Po utworzeniu, pickle
nie respektuje dodawania tych atrybutów (tzn. "zapomina", że zostały dodane-ponieważ pickle
serializuje przez odniesienie do definicji obiektu).
We wszystkich tych przypadkach, pickle
i cPickle
mogą Cię strasznie zawieść.
Jeśli chcesz zapisać object
(arbitralnie utworzony), gdzie masz atrybuty (albo dodane w definicji obiektu, albo później)... najlepszym rozwiązaniem jest użycie dill
, które może serializować prawie wszystko w Pythonie.
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)
...
>>>
Teraz wyłącz i uruchom ponownie...
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'
>>>
UPS ... nie dam rady. Spróbujmy dill
. Dorzucimy inny typ obiektu (a lambda
).
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)
...
>>>
A teraz Przeczytaj plik.
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
>>>
To działa. Powodem, dla którego pickle
zawodzi, a dill
nie, jest to, że dill
traktuje __main__
jak moduł (w większości przypadków), a także może wytrawiać definicje klas zamiast wytrawiać przez odniesienie (jak pickle
robi). Powód dill
Może marynować lambda
jest taki, że nadaje mu nazwę... wtedy może zdarzyć się magia marynowania.
Właściwie, jest łatwiejszy sposób, aby zapisać wszystkie te obiektów, zwłaszcza jeśli masz dużo obiektów, które utworzyłeś. Po prostu wyrzuć całą sesję Pythona i wróć do niej później.
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')
>>>
Teraz wyłącz komputer, idź wypić espresso lub cokolwiek innego i wróć później...
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>
Jedyną poważną wadą jest to, że dill
nie jest częścią standardowej biblioteki Pythona. Jeśli więc nie możesz zainstalować pakietu Pythona na swoim serwerze, nie możesz go użyć.
Jednakże, jeśli jesteś w stanie zainstalować pakiety Pythona w swoim systemie, możesz pobrać najnowszą dill
za pomocą git+https://github.com/uqfoundation/dill.git@master#egg=dill
. I możesz uzyskać najnowszą wersję z pip install dill
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-13 00:54:58
Możesz użyć anycache , aby wykonać zadanie za Ciebie. Rozważa wszystkie szczegóły:
- używa dill jako backend,
który rozszerza moduł python
pickle
do obsługilambda
i wszystkich ładnych funkcje Pythona. - przechowuje różne obiekty do różnych plików i ładuje je poprawnie.
- ogranicza rozmiar pamięci podręcznej
- umożliwia wyczyszczenie pamięci podręcznej
- umożliwia współdzielenie obiektów pomiędzy wieloma przebiegami
- pozwala respektować pliki wejściowe, które wpływ na wynik
Zakładając, że masz funkcję myfunc
, która tworzy instancję:
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)
Anycache wywołuje myfunc
po raz pierwszy i ogrzewa wynik do
plik w cachedir
za pomocą unikalnego identyfikatora (w zależności od nazwy funkcji i jej argumentów) jako nazwa pliku.
W każdym kolejnym uruchomieniu marynowany obiekt jest ładowany.
Jeśli cachedir
jest zachowana pomiędzy uruchomieniami Pythona, to marynowany obiekt jest pobierany z poprzedniego uruchomienia Pythona.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-19 20:27:41