在加载旧对象时使用python的属性

时间:2018-03-09 15:31:16

标签: python properties pickle

我有一个相当大的项目,包括最近需要使用@property装饰器更新的类Foo,以创建自定义的getter和setter方法。

我还在我的硬盘上存储了几个Foo的实例,在某些时候我可能需要重新加载。我的问题是,我无法访问这些旧对象上的属性decoreted属性。

考虑以下示例:

import pickle

# define Class and create instance
class Foo:
    def __init__(self):
        self.val = 1
foo = Foo()

# dump foo into file
with open("foo.pickle", 'wb') as handle:
    pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)

# overwrite and add @property in the class definition
class Foo:
    def __init__(self):
        self._val = "new_foo"

    @property
    def val(self):
        return self._val

    @val.setter
    def val(self, val):
        self._val = val

foo_new = Foo()
print(foo_new.val)

# reload foo
with open("foo.pickle", "rb") as handle:
    foo_old = pickle.load(handle)

# try to access attributes
print(foo_old.val)

最后一行提出:

  

NameError:未定义名称“_val”

我还有哪些选项可以访问归档实例的属性?

修改:在第二个Foo定义的构造函数中将self.val更改为self._val

3 个答案:

答案 0 :(得分:2)

泡菜documentation说:

  

当一个类实例被unickled时,它的__init__()方法通常被调用。

这就是未定义_val属性的原因您可以通过在替换__new__类中定义Foo方法并在其中设置实例属性来解决此问题:

import pickle

# define Class and create instance
class Foo:
    def __init__(self):
        self.val = 1
foo = Foo()

# dump foo into file
with open("foo.pickle", 'wb') as handle:
    pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)

# overwrite and add @property in the class definition
class Foo:
    def __new__(cls, val=None):
        inst = super().__new__(cls)
        inst._val = "new_foo"  if val is None else val
        return inst

    @property
    def val(self):
        return self._val

    @val.setter
    def val(self, val):
        self._val = val

foo_new = Foo()
print(foo_new.val)  # -> new_foo

# reload foo
with open("foo.pickle", "rb") as handle:
    foo_old = pickle.load(handle)

print(foo_old.val)  # -> new_foo

答案 1 :(得分:1)

这可能是彻头彻尾的黑客 - 我不确定。但是,我能够使用以下代码重建从第一个“Foo”类中腌制的对象;

import pickle

class Foo:
    def __init__(self):
        self._val = "new_foo"

    @property
    def val(self):
        try:
            return self._val
        except AttributeError:
            self._val = self.__dict__['val']
            self.__dict__.pop('val')
            return self._val

    @val.setter
    def val(self, val):
        self._val = val


with open("foo.pickle", "rb") as handle:
    foo_old = pickle.load(handle)

print(foo_old.val)

答案 2 :(得分:1)

可能是使用自定义Unpickler,尽管您需要保留旧类(使用不同的名称,如果需要,可以隐藏)并定义将旧类的对象转换为新的那一个。这是一个基本的例子:

a