Когда Python восстанавливает экземпляр класса, извлеченный из хранилища, он воссоздает в памяти объект экземпляра, повторно импортируя класс, используя сохраненные строки с именами класса и модуля; присваивает сохраненный словарь атрибутов новому пустому экземпляру класса и связывает экземпляр с классом. Эти действия выполняются по умолчанию, но имеется возможность изменить этот процесс, определив специальные методы, которые будут вызываться модулем pickle при извлечении и сохранении информации об экземпляре (за подробностями обращайтесь к руководству по библиотеке Python).
Главное здесь то, что класс и хранимые экземпляры отделены друг от друга. Сам класс не хранится вместе со своими экземплярами, а находится в исходном файле модуля Python и импортируется заново, когда загружаются экземпляры.
Для иллюстрации предположим, что класс Person из предыдущего раздела был изменен, как показано в примере 17.3.
Пример 17.3. PP4E\Dbase\person.py (вер сия 2)
объект с информацией о сотруднике: поля + поведение изменения: метод tax теперь является вычисляемым атрибутом
class Person:
def __init__(self, name, job, pay=0): self.name = name self.job = job
self.pay = pay # действительные данные экземпляра
def __getattr__(self, attr): # в person.attr
if attr == ‘tax‘:
return self.pay * 0.30 # вычисляется при попытке обращения else:
raise AttributeError() # другие неизвестные атрибуты
def info(self):
return self.name, self.job, self.pay, self.tax
В этой версии устанавливается новая ставка налога (30%), вводится метод __getattr__ перегрузки операции доступа к атрибуту класса и убран оригинальный метод tax. Поскольку при загрузке экземпляров из хранилища будет импортирована эта новая версия класса, они автоматически приобретут новую реализацию поведения — попытки обращения к атрибуту tax будут перехватываться и в ответ будет возвращаться вычисленное значение:
C:\…\PP4E\Dbase> python
> >> import shelve
> >> dbase = shelve.open(‘cast’) # открыть хранилище
>>>
> >> print(list(dbase.keys())) # в хранилище присутствуют оба объекта [‘bob‘, ‘emily‘]
>>> print(dbase[’emily’])
<person.Person object at 0x019AEE90>
>>>
> >> print(dbase[‘bob‘].tax) # больше не требуется вызывать tax()
21000.0
Так как класс изменился, к атрибуту tax теперь можно обращаться как к простому свойству, не вызывая его как метод. Кроме того, поскольку ставка налога в классе изменена, Бобу придется на этот раз платить больше. Конечно, этот пример искусственный, но при правильном использовании такое разделение классов и постоянно хранимых экземпляров может избавить от необходимости использовать традиционные программы обновления баз данных — чтобы добиться нового поведения, в большинстве случаев можно просто изменить класс, а не каждый хранящийся экземпляр.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011