Версирование маринованного объекта
Я работаю над проектом, в котором у нас есть большое количество объектов, которые сериализуются и сохраняются на диск, используя pickle
/cPickle
,
По мере продвижения проекта (после выпуска для клиентов в полевых условиях), вероятно, что будущие функции / исправления потребуют от нас изменения подписи некоторых из наших постоянных объектов. Это может быть добавление полей, удаление полей или даже просто изменение инвариантов в части данных.
Есть ли стандартный способ пометить объект, который будет отмечен как имеющий определенную версию (например, serialVersionUID
в Java)? В основном, если я восстанавливаю экземпляр Foo версии 234, но текущий код 236, я хочу получить уведомление об отмене выбора. Должен ли я просто пойти дальше и развернуть свое собственное решение (может быть PITA).
Спасибо
2 ответа
pickle
Формат не имеет такой оговорки. Почему бы вам просто не сделать "серийный номер версии" частью атрибутов объекта, чтобы они были выбраны вместе с остальными? Тогда "уведомление" может быть тривиально, если сравнить фактическую и желаемую версию - не понимаю, почему это должен быть PITA.
Рассмотрим следующий классный миксин, предложенный Томашем Фрюбо здесь.
# versionable.py
class Versionable(object):
def __getstate__(self):
if not hasattr(self, "_class_version"):
raise Exception("Your class must define _class_version class variable")
return dict(_class_version=self._class_version, **self.__dict__)
def __setstate__(self, dict_):
version_present_in_pickle = dict_.pop("_class_version")
if version_present_in_pickle != self._class_version:
raise Exception("Class versions differ: in pickle file: {}, "
"in current class definition: {}"
.format(version_present_in_pickle,
self._class_version))
self.__dict__ = dict_
__getstate__
метод вызывается pickle
при травлении и __setstate__
вызывается маринадом при мариновании. Этот смешанный класс можно использовать как подкласс классов, версию которых вы хотите отслеживать. Это должно быть использовано следующим образом:
# bla.py
from versionable import Versionable
import pickle
class TestVersioning(Versionable):
_class_version = 1
t1 = TestVersioning()
t_pickle_str = pickle.dumps(t1)
class TestVersioning(Versionable):
_class_version = 2
t2 = pickle.loads(t_pickle_str) # Throws exception about wrong class version