Сохранение объекта (сохранение данных)
Я создал объект, подобный этому:
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, чтобы выполнить эту работу за вас. Он учитывает все детали:
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.
Для получения дополнительной информации обратитесь к документации