Как использовать укроп для сериализации определения класса?

В ответе на Python pickle: работа с обновленными определениями классов автор пакета dill пишет:

"Хорошо, я добавил эту функцию в укроп в последней версии на github. Реализовано с гораздо меньшими хитростями, чем я думал... просто сериализуйте определение класса с помощью pickle, и вуаля."

Установив dill и поработав с ним, мне не очевидно, как на самом деле использовать эту функциональность в dill. Может ли кто-нибудь привести явный пример? Я хотел бы замариновать экземпляр класса, а также сериализовать определение класса.

(Я новичок в python, и мне эта функциональность кажется чрезвычайно важной, так как при травлении объекта было бы здорово максимально приблизиться к гарантии того, что вы можете смотреть на объект (может быть результатом симуляции) в будущее после того, как определение класса могло измениться, и вы не отслеживали все изменения в легкодоступном виде.)


person user8765    schedule 01.09.2014    source источник


Ответы (2)


Я думаю, вы ищете одну из следующих функций…

Здесь я создаю класс и экземпляр, а затем изменяю определение класса. Защищенный класс и экземпляр по-прежнему не поддаются обработке, потому что dill обрабатывает исходный код класса по умолчанию... и управляет несколькими классами с одинаковыми именами в пространстве имен (это делается просто путем управления ссылками указателя на определения классов).

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x+self.y       
...   y = 1
... 
>>> f = Foo()
>>> _Foo = dill.dumps(Foo)
>>> _f = dill.dumps(f)
>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x*self.z  
...   z = -1 
... 
>>> f_ = dill.loads(_f, ignore=True)
>>> f_.y
1
>>> f_.bar(1)
2
>>> Foo_ = dill.loads(_Foo)
>>> g = Foo_()
>>> g.bar(1)
2

Пикл взорвется на вышеизложенном. Если вы не хотите, чтобы dill явно сериализовал класс и делал то, что делает pickle, вы можете попросить dill выбрать по ссылке с помощью dill.dumps(Foo, byref=True). В качестве альтернативы вы можете динамически решить игнорировать вновь определенный класс, используя ignore=False (по умолчанию).

Теперь, в приведенном ниже случае, мы работаем с новым определением класса и извлекаем исходный код из объекта, а затем сохраняем его в файл. Кроме того, мы можем сбросить исходный код в файл (здесь я использую временный файл), чтобы его можно было импортировать позже.

>>> sFoo = dill.source.getsource(Foo)
>>> print sFoo
class Foo(object):
  def bar(self, x):
    return x*self.z
  z = -1

>>> open('myFoo.py', 'w').write(sFoo)    
>>>
>>> f = dill.temp.dump_source(Foo, dir='.')
>>> f.name
'/Users/mmckerns/dev/tmpM1dzYN.py'
>>> from tmpM1dzYN import Foo as _Foo_
>>> h = _Foo_()
>>> h.bar(2)
-2
>>> from myFoo import Foo as _SFoo_
>>> _SFoo_.z
>>> -1
>>> 

Надеюсь, это поможет.

person Mike McKerns    schedule 02.09.2014
comment
Что касается вашего первого блока кода, я запускаю его, и он взрывается точно так же, как pickle: AttributeError: объект 'Foo' не имеет атрибута 'y'. около строки 25. Слишком плохо, потому что это звучало как пример магии, и это сделало бы хранение укропных объектов в базе данных более удобным в сопровождении. Таким образом, есть огромный вариант использования. Укроп 0.2.7.1, настройки укропа по умолчанию (по ссылке False). Я надеюсь, что это ошибка оператора, но вырезание и вставка полностью соответствуют моим навыкам. - person piccolbo; 01.11.2017
comment
Ошибка специфична для ipython. Вздохнул с облегчением, но все еще озадачивающе... проведу расследование и/или открою тикет - person piccolbo; 01.11.2017
comment
@piccolbo: странно. Я могу повторить поведение в ipython -- значит, они, должно быть, придумывают что-то неожиданное. Кажется, это новое поведение. Хм. Я видел твой билет, спасибо. - person Mike McKerns; 01.11.2017
comment
Как указано в вашем тикете, я обновил dill.settings, чтобы явно иметь возможность переключать поведение, которое вы хотели. Я отредактировал свой ответ выше, чтобы показать это. - person Mike McKerns; 26.03.2018
comment
Я не вижу ссылки на проблему, поэтому вот она github.com/uqfoundation/dill/ Issues/243 Спасибо, Майк. - person piccolbo; 26.03.2018
comment
Я мог бы сэкономить так много времени, если бы они просто правильно объяснили эту опцию игнорирования в документах, как есть, вы получаете от меня голос:) - person samaspin; 13.10.2018

Если бы эта функциональность была так важна, она бы уже была в ядре языка. :-) Итак, нет, это не критично для использования Python в любой расширенной форме - и если у вас есть проект, который основан на возможности повторного создания объектов на основе старых моделей - что возможно, вы должны тщательно обдумать это и, вероятно, сохранить старые модели в явном коде, вместо того, чтобы затем сериализовать.

Мой совет: просто «оставьте это отдельно», пока вы не решите, что вам это действительно нужно, и не сравните его с другими решениями, такими как сильная политика миграции модели.

Тем не менее, я пробовал укроп, и он работает так, как рекламируется: он может сериализовать класс так же, как pickle может делать с обычными объектами, используя вызовы «dump» и «dumps», и перестраивать объект класса с помощью «load» и « нагрузки».

Что, вероятно, сбивает вас с толку, так это то, что сериализация объекта (с помощью pickle или dill) не включает ни его исходный код (т.е. фактические строки текстового кода Python, используемые для определения класса), ни его имя.

Итак, если класс называется «A», когда он сериализуется, если вам нужно это имя после его «откачки», вы должны переназначить это имя ему в глобальном пространстве имен. Его оригинальное имя сохраняется в атрибуте __name__. (и для ваших целей несколько версий одной и той же модели, живущих вместе, привели бы к большому конфликту).

Таким образом:

class A(object):
    ...

import dill

dill.dump(A, open("myfile", "w"))

del A
....
someclass = dill.load(open("myfile"))
print (someclass.__name__)
globals()[someclass.__name__] = someclass
# at this point you have the "A" class back in the global namespace
person jsbueno    schedule 02.09.2014