酸洗不可摧毁的物体

时间:2012-12-03 20:31:10

标签: python pygame pickle save savestate

我正在使用pygame制作绘图程序,我想让用户选择保存程序的确切状态,然后在以后重新加载它。 在这一点上,我保存了我的全局字典的副本,然后迭代,酸洗每个对象。 pygame中有一些对象无法被pickle,但可以转换为字符串并以这种方式进行pickle。我的代码设置为执行此操作,但是通过引用可以访问其中一些不可解析的对象。换句话说,它们不在全局字典中,但它们由全局字典中的对象引用。我想在这次递归中腌制它们,但我不知道如何告诉pickle返回它遇到麻烦的对象,更改它,然后尝试再次腌制它。我的代码非常非常糟糕,如果有一种不同的,更好的方式来做我想做的事情,请告诉我。


surfaceStringHeader = 'PYGAME.SURFACE_CONVERTED:'
imageToStringFormat = 'RGBA'
def save_project(filename=None):
    assert filename != None, "Please specify path of project file"
    pickler = pickle.Pickler(file(filename,'w'))
    for key, value in globals().copy().iteritems():
        #There's a bit of a kludge statement here since I don't know how to 
        #access module type object directly
        if type(value) not in [type(sys),type(None)]   and \
        key not in ['__name__','value','key']          and \
        (key,value) not in pygame.__dict__.iteritems() and \
        (key,value) not in sys.__dict__.iteritems()    and \
        (key,value) not in pickle.__dict__.iteritems(): 
        #Perhaps I should add something to the above to reduce redundancy of
        #saving the program defaults?
            #Refromat unusable objects:
            if type(value)==pygame.Surface:
                valueString = pygame.image.tostring(value,imageToStringFormat)
                widthString = str(value.get_size()[0]).zfill(5)
                heightString = str(value.get_size()[1]).zfill(5)
                formattedValue = surfaceStringHeader+widthString+heightString+valueString
            else:
                formattedValue = value

            try:
                pickler.dump((key,formattedValue))
            except Exception as e:
                print key+':' + str(e)

def open_project(filename=None):
    assert filename != None, "Please specify path to project file"
    unpickler = pickle.Unpickler(file(filename,'r'))
    haventReachedEOF = False
    while haventReachedEOF:
        try:
            key,value = unpickler.load()
            #Rework the unpicklable objects stored 
            if type(value) == str and value[0:25]==surfaceStringHeader:
                value = pygame.image.frombuffer(value[36:],(int(value[26:31]),int(value[31:36])),imageToStringFormat)
            sys.modules['__main__'].__setattr__(key,value)
        except EOFError:
            haventReachedEOF = True

5 个答案:

答案 0 :(得分:3)

简而言之:不要这样做。

在应用程序中腌制所有内容都很混乱,可能会导致问题。从程序中获取所需的数据并手动将其存储在适当的数据格式中,然后通过从该数据创建所需的内容来加载它。

答案 1 :(得分:2)

您希望保存整个程序的状态,以便以后重新加载。这是Pickle的完美用例,我根本没有看到用例的问题。然而,你的方法来挑选globals()命名空间并过滤掉sys,pygame和pickle是不可思议的。通常的模式是让你有一个会话对象。

此外,我认为可能会有一些混淆如何发酵:

  1. 当您挑选一个对象时,它所引用的所有对象 成员变量将自动pickle / unpickled,即
  2. 如果pickle无法序列化一个对象,你应该告诉pickle 如何通过为任何不pickle的对象编写自定义getstate and setstate方法来保存和恢复该对象,所以 嵌套在主服务器中的一个或两个类 会话对象将具有自定义 get / setstate 功能 像重新打开文件句柄这样的设备显然会是这样的 会议之间的差异
  3. 如果需要进行二进制序列化 你不需要将对象转换为字符串,只需使用二进制文件 该对象的 get / setstate 方法中的序列化协议, (ie use Protocol 1
  4. 最后,您的代码应该更像这样:

    session = None
    import pickle
    def startsession():
        globals session
        session = pickle.Unpickler(sessionfilehandle('r')).load()
        if session is None: session = Session() 
    
    def savesession(filename=None):
        globals session
        pickle.Pickler.dump(session,sessionfilehandle('w'))
    
    class Session(object):
        def __init__(self):
            self.someobject=NewObject1()
            #.... plus whole object tree representing the whole game
            self.somedevicehandlethatcannotbepickled=GetDeviceHandle1()  #for example
        def __getstate__(self):
            odict = self.__dict__.copy()
            del odict['somedevicehandlethatcannotbepickled'] #don't pickle this
            return odict
        def __setstate__(self, dict):
            self.__dict__.update(dict)
            self.somedevicehandlethatcannotbepickled=GetDeviceHandle1()
    

答案 2 :(得分:1)

从你的评论来看,听起来你要做的就是为用户提供一个实时的翻译,并保存的状态

那么,将该实时解释器作为子进程运行呢?您希望公开给脚本的对象模型中的任何信息都是显式的(无论是通过multiprocessing共享内存还是某种消息传递API)。

然后,你不需要保存你自己的翻译的完整状态,这是非常困难或不可能的;您以正常方式保存数据模型,然后您可以从外部而不是从内部冻结子解释器。

这显然比你想做的要复杂得多,但我认为任何简单的事情都不会起作用。例如,如果用户有一个带有代码的实时解释器,他们可以对任何东西进行monkeypatch - 甚至是酸洗代码 - 然后会发生什么?您需要对可以保存和恢复的内容进行一些限制 - 如果这些限制足够广泛,我认为您必须从外部进行。

同时,正如评论中所提到的,scipy(或者Enthought附带的一些相关项目)和ipython都有限用例的保存和恢复功能,至少可以为您提供一些代码要研究,但它们的用例可能与你的用例不一样。

答案 3 :(得分:1)

如果你知道所有不可解决的对象类型,那么这个问题的答案中的代码可能会有用“Recursively dir() a python object to find values of a certain type or with a certain value” - 我写了它以回应类似的情况,我知道所有不可解决的对象类型但我无法知道它们在数据结构中的位置。您可以使用此代码来查找它们,将其替换为其他内容,然后使用类似的代码将其取回。

答案 4 :(得分:1)

为此,我使用dill,它可以序列化python中的几乎任何东西。 Dill还有some good tools帮助您了解在代码失败时导致酸洗失败的原因。此外,objgraph对测试套件也非常方便。

>>> import dill
>>> # blah blah blah... your session code here
>>> dill.dump_session('pygame.pkl')
>>>
>>> # and if you get a pickling error, use dill's tools to discover a workaround
>>> dill.detect.badobjects(your_bad_object, depth=1)
>>>
>>> # visualize the references in your bad objects
>>> objgraph.show_refs(your_bad_object, filename='pygame_bad_object.png')