使用Pickle保存对象状态(包含对象的对象)

时间:2016-12-29 17:00:59

标签: python-3.x oop object pickle

我正在试图找出如何使用Pickle将对象序列化为保存文件。我的示例是一个名为World的对象,该对象有一个list(名为objects)可能有数百个不同类类型的实例化对象。

问题是Pickle不允许我序列化World.objects列表中的项目,因为它们未被实例化为World的属性。

当我尝试序列化时:

with open('gsave.pkl', 'wb') as output:
    pickle.dump(world.objects, output, pickle.DEFAULT_PROTOCOL)

我收到以下错误:

_pickle.PicklingError: Can't pickle <class 'world.LargeHealthPotion'>:
attribute lookup LargeHealthPotion on world failed

所以,我的问题是:什么是存储world.objects列表项的替代方法,以便它们是world的属性而不是列出未保存的项目?

更新 我认为我的问题不在于存储对象的位置;而是通过以下操作在LargeHealthPotion类中动态创建类World(和许多其他类):

def __constructor__(self, n, cl, d, c, h, l):
    # initialize super of class type
    super(self.__class__, self).__init__(name=n, classtype=cl, description=d, cost=c,
                                         hp=h, level=l)
    # create the object class dynamically, utilizing __constructor__ for __init__ method
    item = type(item_name,
                (eval("{}.{}".format(name,row[1].value)),),
                {'__init__':__constructor__})
    # add new object to the global _objects object to be used throughout the world
    self._objects[item_name] = item(obj_name, obj_classtype, obj_description, obj_cost,
                                    obj_hp, obj_level)

完成后,我会有一个像<world.LargeHealthPotion object at 0x103690ac8>这样的新对象。我动态地这样做是因为我不想明确地为我的世界中的每种不同类型的对象创建数百种不同类型的类。相反,我在迭代我想要创建的项目名称(带有它的统计数据)时动态创建类。

这引入了一个问题,因为在进行酸洗时,它无法找到对类的静态引用,以便解构或重构对象......所以它失败了。

我还能做什么? (除了为每个我想要实例化到我的世界的对象类型创建文字类引用。)

1 个答案:

答案 0 :(得分:2)

Pickle并不挑选类,它依赖于对类动态生成时不起作用的类的引用。 (this answer具有来自documentation

的适当施加和粗体

所以pickle假设如果你的对象是来自名为world.LargeHealthPotion的类,那么它会检查该名称是否实际上解析为在解开时它能够使用的类,如果它不是你的话由于它不知道如何引用该类,因此无法重新初始化该对象。有几种方法可以解决这个问题:

定义__reduce__以重建对象

我不确定如何向您演示此方法,我需要有关您的设置的更多信息,以建议如何实现此方法,但我可以对其进行描述:

首先,您要创建一个函数或classmethod,它可以根据参数重新创建一个对象(可能需要使用类名,实例变量等)。然后在对象基类上定义__reduce__这将返回该函数以及unpickling时传递给它所需的参数。

将动态类放在全局范围

这是快速而肮脏的解决方案。假设类名与world模块中定义的其他内容不冲突,理论上可以通过globals()[item_name] = item_type将类插入全局范围,但我不建议将其作为长期解决方案,因为它是非常糟糕的做法。

不要使用动态类

这绝对是我认为的方式,而不是使用type构造函数,只需定义一个名为ObjectType的类:

  • 不是 type的子类,因此实例可以腌制。
  • 当一个实例被调用时,构造一个具有对象类型引用的新游戏对象。

假设您有一个名为GameObject的班级,需要cls=<ObjectType object>,您可以设置ObjectType类,如下所示:

class ObjectType:
    def __init__(self, name, description):
        self.item_name = name
        self.base_item_description = description
        #other qualities common to all objects of this type

    def __call__(self, cost, level, hp):
        #other qualities that are specific to each item
        return GameObject(cls=self, cost=cost, level=level, hp=hp)

这里我使用__call__ magic method所以它使用与类cls(params)相同的表示法来创建实例,cls=self将指示(抽象的)GameObject构造函数GameObject的类(类型)基于ObjectType实例self。它不一定是关键字参数,但我不确定如何在不了解您的程序的情况下制作连贯的示例代码。