Можно ли распаковывать экземпляры класса после преобразования из старого в новый стиль?


В python можно ли распаковывать объекты, которые были замаринованы как классы python старого стиля после преобразования в классы нового стиля? (То есть объекты с разными "сигнатурами классов"). *

Например, представьте, что некоторые экземпляры были сохранены как:

class foo: # old style

И затем еще несколько объектов были замаринованы после того, как класс был изменен на наследование от объекта:

class foo(object): # new style

Объекты, которые являются pickle.dump'ed с одним, могут быть pickle.load'ed одним и тем же классом стиля, но ни один из них не будет загружать оба. Я подумайте, что стратегия, используемая pickle, изменяется на основе наследования (класс, который наследует от object, автоматически определяет метод__ reduce__, но без наследования-нет). При попытке загрузить с-наследованием старый соленый огурец из кода без наследования (определение старого стиля), я получаю ту же ошибку аргумента, что и в этом SO question; даже если второй аргумент избыточен, он все равно изменяет "сигнатуру класса" и ожидаемые Аргументы, и предотвращает загрузку. Чтобы решить эту проблему, я был бы рад написать unpickler, хотя я боюсь, что это может включать в себя два отдельных подкласса unpicklers a la docs... Если бы я знал, как правильно распаковывать каждую из них, я бы прекрасно справился с этим.

Небольшая предыстория моей ситуации... Я использую класс TrialHandler для сохранения и перезагрузки нажатий кнопок и времени реакции для экспериментов по поведенческой психологии. Мы преобразовали класс TrialHandler для наследования от более абстрактного, baseTrialHandler, но при этом временно изменил сигнатуру класса, чтобы наследовать от объекта. Однако мы не смогли распаковать старые файлы trial_handler, поэтому он был изменен обратно. Я хотел бы посмотреть на данные из того же эксперимента, который был запущен с обеими версиями пробного обработчика, и поэтому хочу распаковать оба типа сохраненных файлов журнала в одном скрипте.

Кроме того, если я не могу написать пользовательский распаковщик, который будет распаковывать оба объекта, есть ли другой способ их сериализации? Я пытался демпинг прямо на ямл, но похоже, что мне придется зарегистрировать класс как что-то, что может быть ямл'изировано, в любом случае.

Полноеописание проблемы с конкретными ошибками находится в списке рассылки PsychoPy. Любые сообщения в блоге, объясняющие детали промежуточного маринования или даже наследования python, были бы наиболее желательны; ближе всего я нашел хорошее объяснение того, почему маринование небезопасно описывая маринование как "простую виртуальную машину на основе стека", которая является мило, но не настолько, чтобы понять даже основы распаковывания себя.

1 6

1 ответ:

Перебираем части:

  1. в Python нет вещи, называемой "сигнатурой класса", хотя я получил идею из вашего вопроса
  2. не наследование от "объекта" в программе Python 2 должно быть считается ошибкой. Классы, которые не наследуются от объекта-также назывались" старорежимные " классы, где просто держали язык за зубами. обратная совместимость, когда новые классы стилей вводятся в Python 2.2 (около 2000/2001 года) - классы старого стиля намного меньше последовательный и отсутствие многих функций, которые делают сегодняшний python таким хороший язык.

С этим покончено: unpickle попытается десериализовать объекты на основе имени класса-как в <module>.<class>, так и в атрибуте объекта __class__.__name__ во время маринования. Таким образом, можно иметь наивный способ, что на исключение unpickle (это вызывает TypeError), поменять местами доступный класс с тем же именем и повторить операцию unpickle Опять же: ваши классы должны наследовать от "объекта" (более конкретно, иметь метакласс на основе "типа")

Что касается примера, о котором я упомянул, то Юст напишет что-нибудь вроде:

try:
    new_obj = pickle.load(data_stream)
except TypeError: # if you know this was caused due to the new version of "Foo" class:
    current_foo = foomodule.Foo
    foomodule.Foo = foomodule.OldFoo
    new_obj = pickle.load(data_stream)
    foomodule.Foo = current_foo