Несмотря на то, что сериализованные объекты могут переправляться самыми необычными способами, тем не менее, в наиболее обычном случае для сериализации объекта в плоский файл нужно открыть файл в режиме записи и вызвать функцию dump:
C:\…\PP4E\Dbase> python
> >> table = {‘a’: [1, 2, 3],
‘b’: [‘spam’, ‘eggs’],
‘c’: {‘name’:’bob’}}
>>>
> >> import pickle
> >> mydb = open(‘dbase’, ‘wb’)
> >> pickle.dump(table, mydb)
Обратите внимание на наличие здесь вложенных объектов — объект Pick1er способен обрабатывать объекты произвольной структуры. Отметьте также, что файл открывается в двоичном режиме. В Python 3.X это является обязательным условием, потому что сериализованные объекты всегда представлены в виде строки bytes. Чтобы выполнить обратное преобразование в другом сеансе или при последующих запусках приложения, достаточно просто открыть файл и вызвать load:
C:\…\PP4E\Dbase> python
> >> import pickle
> >> mydb = open(‘dbase’, ‘rb’)
> >> table = pickle.load(mydb)
> >> table
{‘a’: [1, 2, 3], ‘c’: {‘name’: ‘bob’}, ‘b’: [‘spam’, ‘eggs’]}
Восстановленный объект имеет то же самое содержимое и ту же структуру, что и оригинал, но он создается в другой области памяти. Это относится и к объектам, воссозданным в том же процессе, и к объектам, воссозданным в другом процессе. Напомню, что для воссозданного объекта выполняется условие ==, но не is:
C:\…\PP4E\Dbase> python
> >> import pickle
> >> f = open(‘temp’, ‘wb’)
> >> x = [‘Hello‘, (‘pickle‘, ‘world‘)] # список с вложенным кортежем
> >> pickle.dump(x, f)
> >> f.close() # закрыть, чтобы записать на диск
>>>
> >> f = open(‘temp’, ‘rb’)
> >> y = pickle.load(f)
> >> y
[‘Hello’, (‘pickle’, ‘world’)]
>>>
> >> x == y, x is y # то же значение, но разные объекты
(True, False)
Чтобы еще больше упростить этот процесс, модуль в примере 17.1 заключает вызовы прямого и обратного преобразований объектов в функции, которые также открывают файлы, хранящие сериализованную форму объекта.
Пример 17.1. PP4E\Dbase\filepickle.py
"Утилиты сохранения и восстановления объектов из плоских файлов" import pickle
def saveDbase(filename, object):
"сохраняет объект в файле"
file = open(filename, ‘wb’)
pickle.dump(object, file) # сохранить в двоичный файл
file.close() # подойдет любой объект, похожий на файл
def loadDbase(filename):
"загружает объект из файла"
file = open(filename, ‘rb’)
object = pickle.load(file) # загрузить из двоичного файла
file.close() # воссоздать объект в памяти
return object
Теперь, чтобы сохранить и извлечь объект, просто вызывайте функции из этого модуля. В примере ниже они используются для манипулирования довольно сложной структурой со множественными ссылками на одни и те же вложенные объекты — вложенный список с именем L сохраняется в файле в единственном экземпляре:
C:\…\PP4E\Dbase> python
> >> from filepickle import *
> >> L = [0]
> >> D = {‘x’:0, ‘y’:L}
> >> table = {‘A‘:L, ‘B‘:D} # присутствуют две ссылки на список L
> >> saveDbase(‘myfile‘, table) # сериализовать в файл
C:\…\PP4E\Dbase> python
> >> from filepickle import *
> >> table = loadDbase(‘myfile’) # загрузить/воссоздать
> >> table
{‘A’: [0], ‘B’: {‘y’: [0], ‘x’: 0}}
> >> table[‘A‘][0] = 1 # изменить совместно используемый объект
> >> saveDbase(‘myfile‘, table) # перезаписать в файл
C:\…\PP4E\Dbase> python
> >> from filepickle import *
> >> print(loadDbase(‘myfile‘)) # изменились оба списка L, как и ожидалось {‘A‘: [1], ‘B‘: {‘y‘: [1], ‘x‘: 0}}
Помимо встроенных типов, таких как списки, кортежи и словари, использовавшихся в примерах до сих пор, сериализовать можно также экзем п ляры клас сов. Тем самым обеспечивается естественный способ связать поведение с хранимыми данными (методы классов обрабатывают атрибуты экземпляров) и простой способ миграции (изменения в классе автоматически будут подхвачены хранимыми экземплярами). Ниже приводится короткая демонстрация в интерактивной оболочке:
>>> class Rec:
def __init__(self, hours):
self.hours = hours
def pay(self, rate=50): return self.hours * rate
>>> bob = Rec(40)
>>> import pickle
>>> pickle.dump(bob, open(‘bobrec’, ‘wb’))
>>>
>>> rec = pickle.load(open(‘bobrec’, ‘rb’))
>>> rec.hours
40
>>> rec.pay()
2000
Принцип действия этого механизма мы подробно рассмотрим, когда будем исследовать хранилища, создаваемые модулем shelve, далее в этой главе — как мы увидим позже, модуль pickle может использоваться непосредственно, но он также является базовым механизмом баз данных shelve и ZODB.
В целом Python может сериализовать почти все, что угодно, за исключением:
• Объектов компилированного программного кода: функций и классов, когда при сериализации известны только их имена, без имен модулей, что не позволяет позднее повторно импортировать их и автоматически подхватить изменения в файлах модулей.
• Экземпляры классов, не выполняющие правила импортируемости: если говорить кратко, классы должны быть доступны для импортирования при загрузке объекта (подробнее об этом будет рассказываться ниже, в разделе «Файлы хранилищ shelve»).
• Экземпляров некоторых встроенных и определяемых пользователем типов, написанных на языке C или зависящих от преходящих состояний операционной системы (например, объекты открытых файлов не могут быть сериализованы).
Если объект не может быть сериализован, возбуждается исключение PicklingError. Напомню, что мы еще вернемся к проблеме сериализуемости объектов и классов, когда будем знакомиться с хранилищами, создаваемыми модулем shelve.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011