使用pickle加载课程状态 [英] Use pickle to load a state for class

查看:109
本文介绍了使用pickle加载课程状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用泡菜弄湿我的脚,所以我写了一些这样的示例代码:

I'm trying to get my feet wet with pickle, so I write a little sample code like this:

class start(tk.Frame):
    def __init__(self,*args,**kwargs):
        tk.Frame.__init__(self,*args,**kwargs)
        frame = tk.Frame(self,width=600,height=600)
        self.val = 0
        self.plusButton = tk.Button(self,text="plus",command=self.plus)
        self.plusButton.pack()
        self.valLabel = tk.Label(self)
        self.valLabel.pack()
        self.saveButton = tk.Button(self,text="save",command=self.save)
        self.saveButton.pack()
        self.loadButton = tk.Button(self,text="load",command=self.load)
        self.loadButton.pack()
    def load(self):
        self.__dict__ = pickle.load(open( "testtesttest.p", "rb" ))
    def plus(self):
        self.val += 1 
        self.valLabel.config(text="%d"%(self.val))
    def save(self):
        pickle.dump(self.__getstate__, open( "testtesttest.p", "wb" ))

    def __getstate__(self):
        return self.__getstate__


if __name__=='__main__':
   root = tk.Tk()

   start(root).pack()
   root.mainloop()

所以这个应用程序的目标是一旦我按下加号按钮,屏幕上的数字就会越来越多.如果保存了该文件,请关闭该窗口,然后重新打开它,然后单击加载"按钮,我将看到上次增加该数字的时间.我对泡菜非常陌生,当前代码将其反馈给我:

So the goal of this app is once I hit the plusbutton, there will be an increasing number on the screen. And if I save it, close the window, reopen it, and hit the load button, I will see the last time the number I increased to. I'm very new to pickle, and the current code gives this back to me:

    Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1550, in __call__return self.func(*args)
File "/Users/caoanjie/pickleDemotry.py", line 18, in load 
self.__dict__ = pickle.load(open( "testtesttest.p", "rb" ))pickle.
UnpicklingError: state is not a dictionary

我想知道这里是什么问题.另外,我在网上看到很多教程或示例代码,它们的功能类似于:

I wonder what the problem is here. Also, I see a lot of tutorials or sample code online that does things like:

with open('save_game.dat', 'wb') as f:
    player= pickle.load

with是什么意思?

推荐答案

您的问题可以简化为根本不使用tkinter的小类:

Your problem can be simplified to a small class that doesn't use tkinter at all:

>>> class Foo:
...     def __getstate__(self):
...         print('getstate')
...         return self.__getstate__
... 
>>> obj = pickle.loads(pickle.dumps(Foo().__getstate__))
getstate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.UnpicklingError: state is not a dictionary

您正在腌制__getstate__实例方法,而不是start类的完整状态.假设您还实现了一个__setstate__方法,该方法知道如何从该信息重建对象,则Python使您能够做到这一点.从文档:

You are pickling the __getstate__ instance method, not the full state of the start class. Python lets you do that, assuming that you also implement a __setstate__ method that knows how to rebuild an object from that information. From the docs:

解开时,如果该类定义了__setstate __(),则会以解开状态进行调用.在那种情况下,不需要状态对象是字典.否则,腌制状态必须是字典,并且其项将分配给新实例的字典.

Upon unpickling, if the class defines __setstate__(), it is called with the unpickled state. In that case, there is no requirement for the state object to be a dictionary. Otherwise, the pickled state must be a dictionary and its items are assigned to the new instance’s dictionary.

当您打开进程时,pickle创建一个新的state实例,但是由于该类没有__setstate__方法,因此pickle会尝试还原该对象的__dict__.之所以失败,是因为取消选择的对象是实例方法,而不是dict.这表明您的方法存在更大的问题.

When you unpickle, pickle creates a new instance of state but since the class has no __setstate__ method, pickle tries to restore the object's __dict__. That fails because the unpickled object is an instance method, not a dict. And this shows a bigger problem with your approach.

pickle重新创建整个对象,但不会还原到现有对象中.在您的情况下,如果您腌制整个start对象,则除了您自己创建的对象外,它还将还原第二个start对象.您可以将该对象的__dict__分配给您的__dict__,但这是一个非常危险的提议.您将松开Frame对象的整个状态,而偏爱您腌制的对象中发生的一切.因为tkinter是一个C扩展模块,所以无论如何都无法使整个对象腌制.

pickle recreates entire objects, it doesn't restore into existing objects. In your case, if you pickled the entire start object, it would restore a second start object in addition to the one you created yourself. You could assign that object's __dict__ to your __dict__, but that is a very risky proposition. You would loose the entire state of your Frame object in favor of what happened to be in the object you pickled. Its likely impossible to pickle the entire object anyway because tkinter is a C extension module.

相反,您应该将要保存和还原的数据与碰巧用于与用户交互的tkinter对象分开.这是一条常见的编程规则:将数据与表示分开.在这里,我有一个保存数据的类,可以将其与tkinter类分开保存和恢复.

Instead, you should separate the data you want to save and restore from the tkinter object you happen to use to interact with the user. This is a common programming rule: separate data from presentation. Here, I have a class holding data and I can save and restore it separate from the tkinter class.

import tkinter as tk
import pickle

class State:
    def __init__(self):
        self.val = 0


class start(tk.Frame):
    def __init__(self,*args,**kwargs):
        tk.Frame.__init__(self,*args,**kwargs)
        frame = tk.Frame(self,width=600,height=600)
        self.state = State()
        self.plusButton = tk.Button(self,text="plus",command=self.plus)
        self.plusButton.pack()
        self.valLabel = tk.Label(self)
        self.valLabel.pack()
        self.saveButton = tk.Button(self,text="save",command=self.save)
        self.saveButton.pack()
        self.loadButton = tk.Button(self,text="load",command=self.load)
        self.loadButton.pack()
    def load(self):
        self.state = pickle.load(open( "testtesttest.p", "rb" ))
        self.valLabel.config(text="%d"%(self.state.val))
    def plus(self):
        self.state.val += 1 
        self.valLabel.config(text="%d"%(self.state.val))
    def save(self):
        pickle.dump(self.state, open( "testtesttest.p", "wb" ), 4)

if __name__=='__main__':
   root = tk.Tk()

   start(root).pack()
   root.mainloop()

这篇关于使用pickle加载课程状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆