Zapisywanie obiektu (trwałość danych)

Stworzyłem taki obiekt:

company1.name = 'banana' 
company1.value = 40

Chciałbym zapisać ten obiekt. Jak mogę to zrobić?

Author: H. Tao, 2010-12-25

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?.

Aby upewnić się, że Twój kod będzie zawsze używał wersji C, gdy jest ona dostępna zarówno w Pythonie 2, jak i 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))
 324
Author: martineau,
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.

Zaczynamy od klasy...]}
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.

 40
Author: Mike McKerns,
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ługi lambda 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.

Aby uzyskać więcej informacji zobacz dokumentacja

 2
Author: c0fec0de,
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