Как замариновать себя?

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

self = cPickle.load(f)

cPickle.dump(self,f,2)

person Community    schedule 25.04.2010    source источник
comment
Я мог бы замариновать dict, но тогда я не смог бы позже изменить класс.   -  person    schedule 26.04.2010
comment
отличное название, +1   -  person pcko1    schedule 05.04.2019


Ответы (5)


Это то, что я в итоге сделал. Обновление __dict__ означает, что мы сохраняем все новые переменные-члены, которые я добавляю в класс, и просто обновляем те, которые были там, когда объект был в последний раз обработан. Это кажется самым простым, сохраняя код сохранения и загрузки внутри самого класса, поэтому вызывающий код просто выполняет object.save().

def load(self):
    f = open(self.filename, 'rb')
    tmp_dict = cPickle.load(f)
    f.close()          

    self.__dict__.update(tmp_dict) 


def save(self):
    f = open(self.filename, 'wb')
    cPickle.dump(self.__dict__, f, 2)
    f.close()
person Community    schedule 16.05.2010
comment
Небольшая придирка к PEP8 — функции должны называться load и save строчными буквами python .org/dev/peps/pep-0008/#названия-функций - person Adam Matan; 17.06.2015
comment
Это merge. Вы должны позвонить self.__dict__.clear(), прежде чем звонить self.__dict__.update(..) - person deepelement; 16.08.2017
comment
Есть ли какая-то конкретная причина, по которой вы не используете инструкции with? - person Disgusting; 04.10.2019
comment
Почему вы используете третий параметр = 2? - person Agustin Barrachina; 18.02.2020
comment
У этого решения есть несколько хороших свойств, но есть один большой недостаток: вам нужно создать объект перед вызовом функции загрузки, и у вас может не быть возможности вызвать конструктор с допустимыми данными. Поэтому создание фабричного метода класса имеет больше смысла. - person emem; 08.06.2020

Часть дампа должна работать, как вы предложили. для части загрузки вы можете определить @classmethod, который загружает экземпляр из данный файл и возвращает его.

@classmethod
def loader(cls,f):
    return cPickle.load(f)

тогда вызывающий абонент сделает что-то вроде:

class_instance = ClassName.loader(f)
person Ofri Raviv    schedule 25.04.2010
comment
Так что вы говорите, что с классом Foo, instance foo я могу сделать foo.Save(), но я не могу сделать foo.Load(), мне пришлось бы сделать foo = foo.Load() -- (методы класса можно вызывать с помощью имя экземпляра или класса) - person ; 26.04.2010
comment
Имеет смысл использовать foo=Foo.load(), а не foo=Foo();foo.load(). например, если у Foo есть некоторые переменные, которые ДОЛЖНЫ быть переданы в init, вам нужно будет создать их для foo=Foo(); или если init выполняет какие-то тяжелые вычисления переменных, хранящихся в экземпляре, это будет напрасно. - person Ofri Raviv; 26.04.2010
comment
Это работает, изначально у меня было foo = Foo('somefilename') И Foo выполнял собственную загрузку своих данных. Теперь я делаю: foo = Foo.Load('somefilename') Если я изменю определение Foo, я все равно смогу использовать новые функции/члены в загруженном рассоле экземпляре foo. - person ; 26.04.2010

Если вы хотите, чтобы ваш класс обновлялся из сохраненного рассола… вам в значительной степени нужно использовать __dict__.update, как вы сделали в своем собственном ответе. Однако это похоже на то, как кошка гоняется за своим хвостом… поскольку вы просите экземпляр по существу «сбросить» себя с предыдущим состоянием.

В вашем ответе есть небольшая поправка. На самом деле вы можете замариновать self.

>>> import dill
>>> class Thing(object):
...   def save(self):
...     return dill.dumps(self)
...   def load(self, obj):
...     self.__dict__.update(dill.loads(obj).__dict__)
... 
>>> t = Thing()
>>> t.x = 1
>>> _t = t.save()
>>> t.x = 2
>>> t.x
2
>>> t.load(_t)
>>> t.x
1

Я использовал loads и dumps вместо load и dump, потому что хотел, чтобы рассол сохранялся в строку. Использование load и dump в файле также работает. И, на самом деле, я могу использовать dill для сохранения экземпляра класса в файл для последующего использования… даже если класс определен интерактивно. Продолжение сверху...

>>> with open('self.pik', 'w') as f:
...   dill.dump(t, f)
... 
>>> 

потом останавливаюсь и перезагружаюсь...

Python 2.7.10 (default, May 25 2015, 13:16:30) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('self.pik', 'r') as f:
...   t = dill.load(f)
... 
>>> t.x
1
>>> print dill.source.getsource(t.__class__)
class Thing(object):
  def save(self):
    return dill.dumps(self)
  def load(self, obj):
    self.__dict__.update(dill.loads(obj).__dict__)

>>> 

Я использую dill, который доступен здесь: https://github.com/uqfoundation

person Mike McKerns    schedule 04.08.2014

здесь, в документации, есть пример того, как замариновать экземпляр. (Найдите пример "TextReader"). Идея состоит в том, чтобы определить методы __getstate__ и __setstate__, которые позволяют вам определить, какие данные необходимо обработать, и как использовать эти данные для повторного создания экземпляра объекта.

person unutbu    schedule 25.04.2010
comment
Это не решает проблему, потому что вы по-прежнему не можете вызвать self = cPickle.load(f) функции Load класса, чтобы заполнить класс загруженными данными. Или, конечно, я могу распарить сами данные класса, но я стараюсь не писать весь этот код и не обновлять его при изменении переменных-членов класса. - person ; 26.04.2010

Вот как я это сделал. Преимущество в том, что вам не нужно создавать новый объект. Вы можете просто загрузить его напрямую.

def save(self):
    with open(self.filename, 'wb') as f:
        pickle.dump(self, f)

@classmethod
def load(cls, filename):
    with open(filename, 'rb') as f:
        return pickle.load(f)

Как это использовать:

class_object.save()
filename = class_object.filename
del class_object

class_object = ClassName.load(filename)

Ниже я обновил ответ полностью работающим минимальным примером. Это может быть адаптировано к вашим собственным потребностям.

import pickle

class Test:

    def __init__(self, something, filename) -> None:
        self.something = something
        self.filename = filename


    def do_something(self) -> None:
        print(id(self))
        print(self.something)

    def save(self):
        with open(self.filename, 'wb') as f:
            pickle.dump(self, f)


    @classmethod
    def load(cls, filename):
        with open(filename, 'rb') as f:
            return pickle.load(f)


test_object = Test(44, "test.pkl")
test_object.do_something()
test_object.save()
filename = test_object.filename
del test_object

recovered_object = Test.load(filename)
recovered_object.do_something()
person Sorin Dragan    schedule 24.09.2020
comment
Не работает. Вы не можете просто сохранить self в рассоле при дампе. - person JAWS007; 09.07.2021
comment
@ JAWS007, теперь я привел полный пример в ответе. - person Sorin Dragan; 09.07.2021
comment
Огромное спасибо! - person JAWS007; 10.07.2021