2016-11-23 2 views
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.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() 

Таким образом, цель этого приложения, когда я ударил плюс, на экране будет увеличиваться число. И если я его сохраню, закройте окно, откройте его и нажмите кнопку загрузки, я увижу последний раз, когда число увеличилось. Я новичок в рассоле, и текущий код дает это ко мне спиной:

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 

Интересно, что проблема здесь. Кроме того, я вижу много учебники или образец кода в Интернете, что делает такие вещи, как:

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

Что with значит?

+0

[ '' 'with''' в Документах] (https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) - при использовании с файловым объектом , он гарантирует, что файл закрыт, даже если возникает исключение. В Интернете есть много ссылок, которые могут помочь - http://effbot.org/zone/python-with-statement.htm. Вот так Q & A - http://stackoverflow.com/q/1369526/2823755 – wwii

+0

Как правило, вы всегда должны публиковать полный ответ в своих вопросах. – wwii

+0

'' 'def __getstate __ (self):' '' выглядит проблематично - что он возвращает, если вы его назовете * вручную *? – wwii

ответ

4

Ваша проблема может быть упрощена до небольшого класса, который не использует Tkinter вообще:

>>> 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 класса. Python позволяет это сделать, предполагая, что вы также реализуете метод __setstate__, который знает, как перестроить объект из этой информации. Из docs:

Upon unpickling, если класс определяет __setstate __(), он вызывается с unpickled состояния. В этом случае для объекта состояния не требуется словарь. В противном случае, маринованное состояние должно быть словарем, а его элементы назначаются в словарь нового экземпляра.

Когда вы unpickle рассольный создает новый экземпляр state, но так как класс не имеет __setstate__ метода, рассол пытается восстановить объект __dict__. Это не удается, потому что незакрашенный объект является методом экземпляра, а не dict. И это показывает большую проблему с вашим подходом.

pickle воссоздает целые объекты, он не восстанавливает существующие объекты. В вашем случае, если вы мариновали весь объект start, он восстановил бы второй объект start в дополнение к тому, который вы создали сами. Вы можете назначить этот объект __dict__ на ваш __dict__, но это очень рискованное предложение. Вы потеряли бы все состояние вашего объекта Frame в пользу того, что случилось с объектом, который вы мариновали. Вероятно, его невозможно раскрыть весь объект, поскольку tkinter является модулем расширения C.

Вместо этого вы должны отделить данные, которые хотите сохранить и восстановить, из объекта tkinter, который вы использовали для взаимодействия с пользователем. Это общее правило программирования: отдельные данные из презентации. Здесь у меня есть класс, содержащий данные, и я могу сохранить и восстановить его отдельно от класса 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() 
+0

Большое вам спасибо! Это очень ясно! – angieShroom

+0

@angieShroom - рад, что я мог бы помочь. Если это сработает для вас, примите это как ответ, чтобы другие знали. – tdelaney

Смежные вопросы