Сохранение объекта (сохранение данных)

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

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

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

6 ответов

Решение

Вы могли бы использовать 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.6 это версия протокола 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...

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

В то время как файл маринования может содержать любое количество маринованных объектов, как показано в приведенных выше примерах, при их неизвестном количестве часто их проще хранить в каком-либо контейнере переменного размера, например 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)

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

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

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

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

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

Быстрый пример использования company1 из вашего вопроса с python3.

import pickle

# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))

Однако, как отмечалось в этом ответе, рассол часто не работает. Так что вам действительно стоит использоватьdill.

import dill

# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))

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

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

Для дальнейших подробностей смотрите документацию

В более новых версиях pandas также есть функция сохранения солений.

Мне так проще. например

      pd.to_pickle(object_to_save,'/temp/saved_pkl.pickle' )

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

      import pickle

class Example:
    def __reduce__(self):
        print("Serialised")
        return print, ("Deserialised",)

example = Example()

serialised = pickle.dumps(example) # prints "Serialised"
pickle.loads(serialised) # prints "Deserialised"

Хотя приведенный выше пример совершенно безвреден, было бы тривиально сделать его намного, намного неприятнее, например, заменивprintсexecилиevalв возвращаемом значении.

На самом деле нет хорошего решения для этого, но люди упомянулиdillбиблиотека как возможная альтернатива . Однако есть и другая альтернатива, т. Это встроенная библиотека сериализации, но у нее есть тот недостаток, что она не работает с некоторыми типами объектов (хотя, в отличие от , она может сериализовать CodeObjects, поэтому она внутренне используется для сериализации вышеупомянутых во время компиляции CodeObjects в.pycфайлы). Это также намного быстрее, будучи на C (код здесь), но не подвергалось тщательному аудиту. Однако об уязвимостях безопасности ничего не известно.

marshalэффективно заменяетpickle. loads,load,dumpsиdumpвсе методы делают одно и то же.

Другие вопросы по тегам