Сохранение объекта (сохранение данных) - PullRequest
192 голосов
/ 25 декабря 2010

Я создал такой объект:

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

Я хотел бы сохранить этот объект. Как я могу это сделать?

Ответы [ 3 ]

383 голосов
/ 25 декабря 2010

Вы можете использовать модуль pickle в стандартной библиотеке. Вот элементарное применение этого к вашему примеру:

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

Вы также можете определить свою собственную простую утилиту, например, такую, которая открывает файл и записывает в него один объект:

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

Обновление:

Поскольку это такой популярный ответ, я бы хотел коснуться нескольких немного продвинутых тем использования.

cPickle (или _pickle) против pickle

Почти всегда предпочтительнее использовать модуль cPickle, а не pickle, потому что первый написан на C и намного быстрее. Между ними есть некоторые тонкие различия, но в большинстве случаев они эквивалентны, и версия C обеспечит значительно более высокую производительность. Переход к нему не может быть проще, просто измените выражение import на следующее:

import cPickle as pickle

В Python 3 cPickle был переименован в _pickle, но делать это больше не нужно, поскольку модуль pickle теперь делает это автоматически - см. Какая разница между pickle и _pickle в python 3? .

Краткое изложение: вы можете использовать что-то вроде следующего, чтобы гарантировать, что ваш код будет всегда использовать версию C, когда она доступна как в Python 2, так и в 3:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Форматы потока данных (протоколы)

pickle может считывать и записывать файлы в нескольких различных, специфичных для Python форматах, называемых протоколами , как описано в документации , "Протокол версии 0" является ASCII и, следовательно, "человек читаемый". Версии> 1 являются двоичными, и максимальная доступная версия зависит от того, какая версия Python используется. Значение по умолчанию также зависит от версии Python. В Python 2 по умолчанию была версия протокола 0, но в Python 3.7 это версия протокола 3. В Python 3.x к модулю добавлен pickle.DEFAULT_PROTOCOL, но в Python 2 его нет.

К счастью, есть сокращение для записи pickle.HIGHEST_PROTOCOL в каждом вызове (при условии, что это то, что вы хотите, и вы обычно делаете это), просто используйте буквенное число -1 - аналогично ссылке на последний элемент последовательности через отрицательный индекс. Итак, вместо того, чтобы писать:

pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

Вы можете просто написать:

pickle.dump(obj, output, -1)

В любом случае, вам нужно было бы указать протокол только один раз, если вы создали объект Pickler для использования в нескольких операциях посола:

pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

Примечание : Если вы работаете в среде, в которой работают разные версии Python, вам, вероятно, захочется явно (т. Е. Жестко закодировать) определенный номер протокола, который все они могут прочитать (более поздние версии) обычно может читать файлы, созданные ранее).

Несколько объектов

Несмотря на то, что файл маринованных файлов может содержать любое количество маринованных объектов, как показано в приведенных выше примерах, при их неизвестном количестве часто проще хранить их все в файлах различного размера. контейнер, например list, tuple или dict и записать их все в файл за один вызов:

tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')

и восстановить список и все, что в нем будет позже, с помощью:

with open('tech_companies.pkl', 'rb') as input:
    tech_companies = pickle.load(input)

Основным преимуществом является то, что вам не нужно знать, сколько экземпляров объектов сохранено, чтобы загрузить их позже (хотя без этой информации возможно , возможно, потребуется немного специализированный код) , См. Ответы на соответствующий вопрос Сохранение и загрузка нескольких объектов в файл рассылки? для получения подробной информации о различных способах сделать это. Лично Я , как @Lutz Prechelt's answer лучший. Вот он адаптированный к примерам здесь:

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))
43 голосов
/ 04 августа 2014

Я думаю, что довольно сильное предположение, что объект является class. Что если это не class? Есть также предположение, что объект не был определен в интерпретаторе. Что, если это было определено в интерпретаторе? Кроме того, что если атрибуты были добавлены динамически? Когда некоторые объекты Python имеют атрибуты, добавленные к их __dict__ после создания, pickle не учитывает добавление этих атрибутов (т. Е. «Забывает», что они были добавлены - потому что pickle сериализуется со ссылкой на определение объекта) .

Во всех этих случаях pickle и cPickle могут вас ужасно подвести.

Если вы хотите сохранить object (произвольно созданный), где у вас есть атрибуты (добавленные в определении объекта или после него) ... лучше всего использовать dill, который может сериализовать почти все в питон.

Начнем с класса…

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

Теперь выключите и перезапустите ...

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'
>>> 

Упс ... pickle не справится. Давайте попробуем dill. Мы добавим другой тип объекта (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)
... 
>>> 

А теперь прочитайте файл.

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

Работает. Причина сбоя pickle и dill заключается в том, что dill обрабатывает __main__ как модуль (по большей части), а также может выбирать определения классов вместо выбора по ссылке (например, pickle). делает). Причина, по которой dill может засолить lambda, заключается в том, что он дает ему имя ... тогда может произойти магия засолки.

На самом деле, есть более простой способ сохранить все эти объекты, особенно если у вас есть много созданных вами объектов. Просто выкиньте весь сеанс Python и вернитесь к нему позже.

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

Теперь выключите компьютер, выпейте эспрессо или еще что-нибудь и зайдите позже ...

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>

Единственным существенным недостатком является то, что dill не является частью стандартной библиотеки Python. Поэтому, если вы не можете установить пакет Python на свой сервер, вы не сможете его использовать.

Однако, если вы можете установить пакеты Python в своей системе, вы можете получить самую последнюю версию dill с git+https://github.com/uqfoundation/dill.git@master#egg=dill. И вы можете получить последнюю выпущенную версию с pip install dill.

3 голосов
/ 19 ноября 2017

Вы можете использовать anycache , чтобы сделать работу за вас.Он учитывает все детали:

  • Он использует укроп в качестве бэкэнда, что расширяет модуль python pickle для обработки lambda и все приятные возможности Python.
  • Хранит разные объекты в разных файлах и правильно их перезагружает.
  • Ограничивает размер кеша
  • Разрешает очистку кеша
  • Разрешает совместное использование объектов между несколькими запусками
  • Позволяет учитывать входные файлы, которые влияют на результат

Предполагается, что у вас есть функция myfunc, которая создает экземпляр:

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 вызывает myfunc при первомtime и выбирает результат в файл в cachedir, используя уникальный идентификатор (в зависимости от имени функции и ее аргументов) в качестве имени файла.При любом последовательном прогоне засоленный объект загружается.Если cachedir сохраняется между запусками Python, выбранный объект берется из предыдущего запуска Python.

Более подробную информацию см. В документации

...