Интерфейс модуля shelve так же прост, как и интерфейс модуля pickle: хранилища, создаваемые модулем shelve, идентичны словарям с дополнительными методами open и close. В программном коде объекты хранилищ действительно выглядят, как словари, содержимое которых сохраняется после завершения программы. А все операции по отображению содержимого хранилища в файл и из файла выполняются интерпретатором Python. Например, сценарий в примере 1.11 демонстрирует, как можно сохранить объекты из словаря в хранилище, созданном с помощью модуля shelve.
Пример 1.11. PP4E\Preview\make_db_shelve.py
from initdata import bob, sue import shelve
db = shelve.open(‘people-shelve’) db[‘bob’] = bob db[‘sue’] = sue db.close()
Этот сценарий создаст в текущем каталоге один или более файлов, имена которых начинаются с префикса people-shelve (в ОС Windows, под управлением Python 3.1, сценарий создаст файлы people-shelve.bak, people-shelve.dat и people-shelve.dir). Вы не должны удалять эти файлы (они составляют вашу базу данных!), а чтобы получить доступ к этому хранилищу в других сценариях, необходимо использовать то же самое имя базы. Сценарий в примере 1.12, например, повторно открывает хранилище и последовательно извлекает хранящиеся в нем записи.
Пример 1.12. PP4E\Preview\dump_db_shelve.py
import shelve
db = shelve.open(‘people-shelve’) for key in db:
print(key, ‘=>\n ‘, db[key]) print(db[‘sue’][‘name’]) db.close()
Мы по-прежнему имеем словарь словарей, но на этот раз словарем верхнего уровня является хранилище, отображаемое в файл. Всякий раз, когда происходит обращение к элементу в хранилище, модуль shelve выполняет необходимые операции с файловой системой, обеспечивающей доступ по ключу, и использует модуль pickle для сериализации и десериализации объектов. Однако, с точки зрения программиста, хранилище - это всего лишь словарь, обладающий возможностью сохраняться между вызовами программы. Сценарий в примере 1.13 демонстрирует, как можно реализовать изменение данных в хранилище.
Пример 1.13. PP4E\Preview\update_db_shelve.py
from initdata import tom import shelve
db = shelve.open(‘people-shelve’)
sue = db[‘sue’] # извлекает объект sue
sue[‘pay’] *= 1.50
db[‘sue’] = sue # изменяет объект sue
db[‘tom’] = tom # добавляет новую запись
db.close()
Обратите внимание, что в этом примере сначала по ключу извлекается объект sue, затем он изменяется в памяти и снова сохраняется в хранилище по ключу. Так действуют хранилища по умолчанию, однако более совершенные системы хранения, такие как ZODB, о которой рассказывается в главе 17, могут действовать иначе. Как мы узнаем позднее, метод shelve.open в подобных системах имеет дополнительный именованный аргумент writeback. Если в этом аргументе передать значение True, все загруженные записи будут сохраняться в кэше и автоматически записываться обратно в файл при закрытии хранилища. Благодаря этому не требуется вручную записывать изменения обратно в хранилище, но при этом увеличивается потребление памяти, а сама операция закрытия может занимать продолжительное время.
Обратите также внимание на необходимость явного закрытия хранилища. Нам не требуется указывать флаги режимов в вызове метода shelve. open (по умолчанию он создает новое хранилище, если это необходимо, и открывает существующее хранилище для чтения и записи), однако некоторые механизмы, обеспечивающие доступ к содержимому файлов по ключу, требуют вызова метода close, чтобы сбросить на диск выходные буферы с изменениями.
Наконец, ниже приводится пример запуска сценариев, опирающихся на использование модуля shelve, которые создают, изменяют и извлекают записи. Записи по-прежнему реализованы как словари, но сама база данных теперь является хранилищем, напоминающим словарь, который автоматически сохраняет свое содержимое в файле:
...\PP4E\Preview> python make_db_shelve.py
...\PP4E\Preview> python dump_db_shelve.py
bob =>
{‘pay’: 30000, ‘job’: ‘dev’, ‘age’: 42, ‘name’: ‘Bob Smith’}
sue =>
{‘pay’: 40000, ‘job’: ‘hdw’, ‘age’: 45, ‘name’: ‘Sue Jones’}
Sue Jones
...\PP4E\Preview> python update_db_shelve.py
...\PP4E\Preview> python dump_db_shelve.py
bob =>
{‘pay’: 30000, ‘job’: ‘dev’, ‘age’: 42, ‘name’: ‘Bob Smith’}
sue =>
{‘pay’: 60000.0, ‘job’: ‘hdw’, ‘age’: 45, ‘name’: ‘Sue Jones’}
tom =>
{‘pay': 0, ‘job': None, ‘age': 50, ‘name': ‘Tom'}
Sue Jones