Как перезаписать методы дампа/загрузки в классе рассола — настройка травления и распаковки — Python

Пока что я сделал следующее:

import pickle

class MyPickler(pickle.Pickler):
    def __init__(self, file, protocol=None):
        super(MyPickler, self).__init__(file, protocol)

class MyUnpickler(pickle.Unpickler):
    def __init__(self, file):
        super(MyUnpickler, self).__init__(file) 

В моем основном методе это в основном то, что у меня есть

#created object, then... 
pickledObject = 'testing.pickle'
with open(pickledObject,'wb') as f:
    pickle = MyPickler(f)
    pickle.dump(object) #object is the object I want to pickle, created before this

with open(pickledObject, 'r') as pickledFile:
    unpickle = MyUnpickler(pickledFile)
    object2 = unpickle.load()

Однако это дает мне следующую ошибку при вызове суперметода: TypeError: must be type, not classobj

Как перезаписать только два метода: загрузку и дамп? Файл рассола находится в папке C:\Python27/lib/pickle.py.

ИЗМЕНИТЬ Файл enum.py можно найти здесь: http://dpaste.com/780897/

Сведения об объекте: объект инициализируется следующим образом:

object = CellSizeRelation(CellSizeRelation.Values.FIRST)

И CellSizeRelation — это класс, который использует Enumeration:

class CellSizeRelation(Option):
    Values = enum.Enum('FIRST',
                       'SECOND')

Прежде чем мариновать объект, я делаю это:

print object.Values._values 
print object.value.enumtype 

вывод

[EnumValue(<enum.Enum object at 0x02E80E50>, 0, 'FIRST'), EnumValue(<enum.Enum object at 0x02E80E50>, 1, 'SECOND')
<enum.Enum object at 0x02E80E50>

После того, как я распаковал и распечатал то же самое, я получил вывод:

[EnumValue(<enum.Enum object at 0x02E80E50>, 0, 'FIRST'), EnumValue(<enum.Enum object at 0x02E80E50>, 1, 'SECOND')
<enum.Enum object at 0x02ECF750>

Проблема в том, что адрес второго объекта меняется; При первой инициализации enumtype и _values имеют одинаковый адрес. Однако после распаковки они меняют адреса. Это ломает мой код, когда я пытаюсь сравнить два значения enumValues. Если вы посмотрите на класс enumValue, функция сравнения попытается сделать это:

try:
        assert self.enumtype == other.enumtype
        result = cmp(self.index, other.index)

Поскольку адрес изменяется, функция утверждения завершается ошибкой. Мне теперь как-то нужно убедиться, что адрес для enumtype не меняется при распаковывании. Я думал просто получить значение «ПЕРВЫЙ» из нераспакованного файла, узнать его индекс и повторно инициализировать объект с помощью:

def load:
    object = CellSizeRelation(CellSizeRelation.Values[INDEX])
    return object

person SaiyanGirl    schedule 03.08.2012    source источник
comment
Из любопытства, чего вы пытаетесь достичь?   -  person Martijn Pieters    schedule 03.08.2012
comment
Во-первых, есть ли причина, по которой вам нужно использовать здесь наследование/подкласс вместо композиции/агрегации? Если это так, вам придется иметь дело с тем фактом, что pickle.Pickler не обязательно является классом нового стиля в Python 2.x, и из ошибки кажется, что в вашем случае это не так, что делает вещи сложнее, чем вы, вероятно, хотели бы иметь дело, если вы еще не знаете разницу между type и classobj.   -  person abarnert    schedule 03.08.2012
comment
@MartijnPieters У меня есть класс перечисления (не написанный мной), и он содержит значения. Перечисление в значительной степени представляет собой список, и у класса есть несколько методов. Ну, когда я инициализирую объект перечисления типом из списка, адрес типа перечисления и объекта внутри совпадают. Однако после распаковки эти адреса меняются, что еще больше разбивает мой код. Итак, цель состоит в том, чтобы сделать эти два адреса одинаковыми, когда я распаковываю... Честно говоря, я даже не слишком уверен, что я действительно пытаюсь сделать, но я надеюсь, что мое объяснение прояснило некоторые вещи?   -  person SaiyanGirl    schedule 03.08.2012
comment
@abarnert Я новичок (первые месяцы на своей первой работе), поэтому эта ошибка определенно меня озадачивает. Нет особой причины, по которой я должен использовать наследование, но я надеялся, что это будет самый простой способ решить проблему. Как бы я использовал композицию/агрегацию?   -  person SaiyanGirl    schedule 03.08.2012
comment
@Dana: Вы можете сообщить, как выглядят эти классы перечисления и что идет не так. Однако создание подклассов сборщика и распаковщика почти наверняка не лучший вариант.   -  person Martijn Pieters    schedule 03.08.2012
comment
@MartijnPieters - Хорошо, я изменю свой вопрос. Я буду готов через 5 минут   -  person SaiyanGirl    schedule 03.08.2012
comment
Причина, по которой я получаю TypeError, заключается в том, что исходные классы Pickler и Unpickler не наследуются от объекта (я исследовал после того, как прочитал комментарий abarnert). Если я попытаюсь изменить любой класс для наследования от объекта, файл не позволит мне сохранить.   -  person SaiyanGirl    schedule 03.08.2012
comment
Обратите внимание, что enum.py, которым вы поделились, в основном представляет собой пакет enum, опубликованный на PyPI: pypi.python.org/pypi/enum   -  person Martijn Pieters    schedule 04.08.2012
comment
@MartijnPieters - да, я понял после того, как прочитал начало своего файла, что он был сделан кем-то общедоступным - вот почему я не возражал опубликовать код :)   -  person SaiyanGirl    schedule 04.08.2012


Ответы (2)


Вы хотите настроить способ выделения и извлечения состояния объекта, а не настраивать функции загрузки и выгрузки.

Вам придется изучить маринование и рассол главы об экземплярах классов, в вашем случае должно быть достаточно определения методов __getstate__ и __setstate__.

Что происходит в вашем случае, так это то, что существует атрибут уровня класса с экземплярами EnumValue, которые должны быть константами. Но при распаковке создаются новые экземпляры EnumValue, которые больше не связаны с атрибутом уровня класса.

Экземпляры EnumValue имеют атрибут index, который вы можете использовать для захвата их состояния в виде целого числа вместо экземпляра EnumValue, который мы можем использовать, чтобы снова найти правильную константу при восстановлении ваших экземпляров:

 class CellSizeRelation(Option):
     # skipping your enum definition and __init__ here

     def __getstate__(self):
         # capture what is normally pickled
         state = self.__dict__.copy()
         # replace the `value` key (now an EnumValue instance), with it's index:
         state['value'] = state['value'].index
         # what we return here will be stored in the pickle
         return state

     def __setstate__(self, newstate):
         # re-create the EnumState instance based on the stored index
         newstate['value'] = self.Values[newstate['value']]
         # re-instate our __dict__ state from the pickled state
         self.__dict__.update(newstate)

Итак, обычно, если __getstate__ нет, экземпляр __dict__ травится. Теперь мы возвращаем копию этого __dict__, но мы заменили экземпляр EnumValue на его индекс (простое целое число). При распаковке обычно новый экземпляр __dict__ обновляется с помощью нераспакованного __dict__, который мы зафиксировали при травлении, но теперь, когда мы определили __setstate__, мы можем снова поменять индекс перечисления на правильный EnumValue.

person Martijn Pieters    schedule 03.08.2012

EnumValue зависит от id идентичности между Enum объектами "типа перечисления". Это имеет некоторые преимущества и недостатки.

Основное преимущество состоит в том, что два вызова Enum('A', 'B') определяют разные типы перечисления. Так:

osx = Enum('Jaguar', 'Tiger', 'Leopard')
bigcats = Enum('Jaguar', 'Tiger', 'Leopard')

Если вы хотите отличить OS X 10.4 от полосатой машины для убийств, это может быть полезно.

Но это также означает, что когда pickle распаковывает osx и bigcats, они будут отличаться не только друг от друга, но и от любых более ранних экземпляров osx и bigcats. Там действительно сейчас что-то вокруг этого, если подумать.

Итак, ваше решение не может включать в себя какой-либо хакерский рассол; придется взломать модуль enum.

Вам нужно будет определить разумный __cmp__ метод для Enum, который делает то, что имеет для вас смысл. Если вы можете отказаться от различия osx-vs.-bigcats, это легко. Если вы не можете, вам нужен какой-то другой способ (может быть, добавление явного имени тега в определение enum или необязательный, но в противном случае неявно-инкрементный счетчик?), который обрабатывает это.

person abarnert    schedule 03.08.2012
comment
Нет, вы можете взломать захват и восстановление состояния. - person Martijn Pieters; 03.08.2012
comment
Да, вы можете, но вы не можете заставить его генерировать тот же идентификатор, что и у уже существующего объекта, так какой в ​​этом смысл? - person abarnert; 04.08.2012
comment
Вы можете получить исходный объект, который все еще там. Обратите внимание на мой ответ; мы избегаем всей проблемы, не собирая значения перечисления, вместо этого мы сохраняем их индекс, а затем восстанавливаем перечисление при распаковывании. - person Martijn Pieters; 04.08.2012
comment
Каждый вновь распакованный Enum — это уникальный объект, не равный никакому другому. Вам нужен какой-то способ определить, экземпляром какого Enum вы хотите, чтобы ваш EnumValue был экземпляром. Вы можете сделать это ответственным за каждый объект, которому принадлежит EnumValue, или вы можете изменить Enum, как я описал выше, но вы должны сделать это где-то. (И вы можете получить исходный объект только в том случае, если вы выбираете и распаковываете в одном и том же процессе. Очевидно, что после выхода и перезапуска исходный объект больше не существует.) - person abarnert; 04.08.2012
comment
@abarnert Эй, абарнерт! Прежде всего, спасибо за ответ! Я сделал функцию сравнения внутри CellSizeRelation, но мой наставник сказал мне, что было бы лучше, если бы я каким-то образом перезаписал метод травления, поэтому мне не нужно создавать новые методы сравнения для всех моих перечислений. Я также подумывал об изменении функции сравнения, но мой наставник высказался против этого — я думаю, недостатком вашей первой работы/стажировки является то, что у вас отнимается много творчества =/. Хотя еще раз спасибо! - person SaiyanGirl; 04.08.2012