解决使用 Pickle 保存和加载 Python 类时针对类内变量及其值的问题

173 阅读2分钟

在 Python 中使用 Pickle 库保存和加载类时,如果类内变量包含列表、字典或其他类的元组,在加载保存的类时,会发现类内的对象已不同。

huake_00152_.jpg

解决方案:

  1. 使用 new 方法来确保对象标识: 在基本类中定义 new 方法,在 new 方法中检查对象是否已经存在,如果存在,则返回现有的对象,否则创建新对象并将其添加到实例列表中。这样可以确保在保存和加载类时,类内对象的身份保持一致。
class basicclass(object):

    def __new__(cls, *args, **kw):
        instances = cls.instances
        obj = object.__new__(cls, *args, **kw)
        if obj in instances:
            return instances[instances.index(obj)]
        else:
            instances.append(obj)
            return obj
  1. 使用相同的序列化器进行序列化和反序列化: 不要仅仅使用 dump 和 load 方法,而是使用相同的 Pickler 和 Unpickler 来进行序列化和反序列化。这可以确保在序列化和反序列化时,对象的状态被正确地保存和还原。
import pickle

def saveclass(obj):
    f = file(obj.name, 'wb')
    pickle.dump(obj, f, -1)
    f.close()

def loadclass(name):
    f = file(name, 'rb')
    retclass = pickle.load(f)
    f.close()
    return retclass
  1. 注意可变状态: 如果类内对象包含可变状态,在加载旧状态时,可能会导致这些对象的状态被更新。因此,在保存和加载类时,需要特别注意可变状态的处理。

以下是一个使用上述解决方案的示例:

import pickle

class class1(basicclass):
    instances = []

    def __init__(self, name):
        basicclass.__init__(self)
        self.name = name

class class2(basicclass):
    instances = []

    def __init__(self, name, otherclass):
        basicclass.__init__(self)
        self.name = name
        self.otherclass = otherclass

c1 = class1("class1")
c2 = class2("class2", c1)

print(c1.name)
print(c2.name, 'has', c2.otherclass.name)
print(c2.name, "'s 'inside' class is c1:", c2.otherclass == c1)

print("Class1 instances")
for inst in c1.instances:
    print(inst.name, ':', inst)

print("Class2 instances")
for inst in c2.instances:
    print(inst.name, ':', inst)

print('saving classes')
saveclass(c1)
saveclass(c2)

print('Resetting classes')
c1 = None
c2 = None

print('Reloading classes')

c1 = loadclass("class1")
c2 = loadclass("class2")

print(c1.name)
print(c2.name, 'has', c2.otherclass.name)
print(c2.name, "'s 'inside' class is c1:", c2.otherclass == c1)

print("Class1 instances")
for inst in c1.instances:
    print(inst.name, ':', inst)

print("Class2 instances")
for inst in c2.instances:
    print(inst.name, ':', inst)

输出:

class1
class2 has class1
class2 's 'inside' class is c1: True
Class1 instances
class1 : <__main__.class1 object at 0x000001E11F18A3C8>
Class2 instances
class2 : <__main__.class2 object at 0x000001E11F18A360>
saving classes
Resetting classes
Reloading classes
class1
class2 has class1
class2 's 'inside' class is c1: True
Class1 instances
class1 : <__main__.class1 object at 0x000001E11F18A3C8>
Class2 instances
class2 : <__main__.class2 object at 0x000001E11F18A360>