Usa pickle para cargar un estado para la clase

Estoy tratando de mojarme los pies con el pepinillo, así que escribo un pequeño código de ejemplo como este:

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() 

Así que el objective de esta aplicación es que una vez que golpee el botón más, habrá un número creciente en la pantalla. Y si lo guardo, cierro la ventana, la vuelvo a abrir y presiono el botón de carga, veré la última vez que aumenté el número. Soy muy nuevo en pickle, y el código actual me lo devuelve:

  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 

Me pregunto cuál es el problema aquí. Además, veo muchos tutoriales o códigos de muestra en línea que hacen cosas como:

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

¿Qué significa with ?

Su problema se puede simplificar a una clase pequeña que no usa tkinter en absoluto:

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

Está __getstate__ método de instancia __getstate__ , no el estado completo de la clase de start . Python te permite hacer eso, asumiendo que también implementas un método __setstate__ que sabe cómo reconstruir un objeto a partir de esa información. De los documentos :

Al descomprimir, si la clase define __setstate __ (), se llama con el estado no seleccionado. En ese caso, no hay ningún requisito para que el objeto de estado sea un diccionario. De lo contrario, el estado encurtido debe ser un diccionario y sus elementos se asignan al diccionario de la nueva instancia.

Cuando se desmarca, pickle crea una nueva instancia de state pero como la clase no tiene un método __setstate__ , pickle intenta restaurar el __dict__ del objeto. Eso falla porque el objeto no seleccionado es un método de instancia, no un dict . Y esto muestra un problema más grande con su enfoque.

pickle recrea objetos enteros, no se restaura en objetos existentes. En su caso, si decapara todo el objeto de start , se restaurará un segundo objeto de start además del que usted mismo creó. Podría asignar el __dict__ ese objeto a su __dict__ , pero esa es una proposición muy arriesgada. Perdería todo el estado de su objeto Marco a favor de lo que sucedió en el objeto que esculpió. De todos modos, es probable que sea imposible encurtir todo el objeto porque tkinter es un módulo de extensión C.

En su lugar, debe separar los datos que desea guardar y restaurar del objeto tkinter que utiliza para interactuar con el usuario. Esta es una regla de progtwigción común: datos separados de la presentación. Aquí, tengo una clase que contiene datos y puedo guardarla y restaurarla por separado de la clase tkinter.

 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()