Вопрос-Ответ

Saving an Object (Data persistence)

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

Я создал объект, подобный этому:

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

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

Переведено автоматически
Ответ 1

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

import pickle

class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value

with open('company_data.pkl', 'wb') as outp:
company1 = Company('banana', 40)
pickle.dump(company1, outp, pickle.HIGHEST_PROTOCOL)

company2 = Company('spam', 42)
pickle.dump(company2, outp, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as inp:
company1 = pickle.load(inp)
print(company1.name) # -> banana
print(company1.value) # -> 40

company2 = pickle.load(inp)
print(company2.name) # -> spam
print(company2.value) # -> 42

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

def save_object(obj, filename):
with open(filename, 'wb') as outp: # Overwrites any existing file.
pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')

Обновить

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

cPickle (или _pickle) vs pickle

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

import cPickle as pickle

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

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

try:
import cPickle as pickle
except ModuleNotFoundError:
import pickle

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

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

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

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

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

pickle.dump(obj, outp, -1)

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

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

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

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

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

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

class Company:
def __init__(self, name, value):
self.name = name
self.value = value

def pickle_loader(filename):
""" Deserialize a file of pickled objects. """
with open(filename, "rb") as f:
while True:
try:
yield pickle.load(f)
except EOFError:
break

print('Companies in pickle file:')
for company in pickle_loader('company_data.pkl'):
print(' name: {}, value: {}'.format(company.name, company.value))
Ответ 2

Я думаю, было бы довольно сильным предположением предположить, что объект является a class. Что, если это не a 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. Для пущей убедительности добавим другой тип объекта (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)
...
>>>

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

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

It works. The reason pickle fails, and dill doesn't, is that dill treats __main__ like a module (for the most part), and also can pickle class definitions instead of pickling by reference (like pickle does). The reason dill can pickle a lambda is that it gives it a name… then pickling magic can happen.

Actually, there's an easier way to save all these objects, especially if you have a lot of objects you've created. Just dump the whole python session, and come back to it later.

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

Now shut down your computer, go enjoy an espresso or whatever, and come back later...

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>

The only major drawback is that dill is not part of the python standard library. So if you can't install a python package on your server, then you can't use it.

However, if you are able to install python packages on your system, you can get the latest dill with git+https://github.com/uqfoundation/dill.git@master#egg=dill. And you can get the latest released version with pip install dill.

Ответ 3

Краткий пример использования 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"))

Однако, как отмечалось в этом ответе, pickle часто дает сбой. Так что вам действительно следует использовать 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"))
Ответ 4

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


  • Он использует dill в качестве серверной части, которая расширяет pickle модуль для обработкиlambda python и все приятные функции 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 сохраняется между запусками python, обработанный объект берется из предыдущего запуска python.

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

2023-10-24 05:42 python