Сохранение объекта (сохранение данных)
Я создал такой объект:
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
все методы делают одно и то же.