如何取消 Python 在 class 将成员变量更改为 属性 之前保存的实例?

How to unpickle a Python instance that was saved before the class changed a member variable to a property?

我有一个 class,它曾经有一个字段 data,但后来 class 被更改了,现在 data 是一个 属性。

我希望能够取消在更改之前被 pickle 的实例,以保持向后兼容性。一个最小的说明示例(在 Python 2 中,尽管在 Python 3 中应该相同):

import pickle

class X(object):
    def __init__(self):
        self.data = 100

pickle.dump(X(), open("x-file",'w'))

# Redefine the class
class X(object):
    def __init__(self):
        self._data = 101
    @property
    def data(self):
        return self._data

y = pickle.load(open("x-file")) # cannot access the original data through y
print(y.data)

我想要的是定义一个函数 load 来解开对象,检测它是旧样式(例如,通过查看它没有 _data 字段),以及 return 一个带有数据的新样式实例。但是,由于字段 data 现在是 属性,旧的 data 字段被 class 定义覆盖。

有没有什么简单的方法可以访问旧数据(例如,除了自己解析泡菜文件)?

编辑 在 Peter Wood 的回答之后,我找到了这个解决方案:

import pickle

class X(object):
    def __init__(self):
        self.data = 100

pickle.dump(X(), open("x-file",'w'))

# Redefine the class
class X(object):
    def __init__(self):
        self._data = 101
    @property
    def data(self):
        return self._data
    def __setstate__(self, state):
        if not '_data' in state:
            self._data = state['data']
            del state['data']
        self.__dict__.update(state)

y = pickle.load(open("x-file")) # cannot access the original data through y
print(y.data)

documentation says in What can be pickled and unpickled?:

If you plan to have long-lived objects that will see many versions of a class, it may be worthwhile to put a version number in the objects so that suitable conversions can be made by the class’s __setstate__() method.

这是您可以在 类 上定义以促进序列化的四种“神奇”方法之一。参见 Pickling Class Instances