Python中自定义对象数组的pickle类型错误
我有一个方法:
def save(self):
vertices = self.mindmap.get_vertices()
edges = self.mindmap.get_edges()
output = open('mindmap.pkl', 'wb')
pickle.dump(edges, output)
pickle.dump(vertices, output)
output.close()
当我调用这个方法时,出现了以下错误:
Traceback (most recent call last):
File "main.py", line 72, in <module>
MindMapApp().run()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/app.py", line 792, in run
runTouchApp()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 481, in runTouchApp
EventLoop.window.mainloop()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/window_pygame.py", line 381, in mainloop
self._mainloop()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/window_pygame.py", line 287, in _mainloop
EventLoop.idle()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 324, in idle
self.dispatch_input()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 309, in dispatch_input
post_dispatch_input(*pop(0))
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 275, in post_dispatch_input
wid.dispatch('on_touch_up', me)
File "_event.pyx", line 316, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:4537)
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/behaviors.py", line 110, in on_touch_up
self.dispatch('on_release')
File "_event.pyx", line 312, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:4491)
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/lang.py", line 1299, in custom_callback
exec(__kvlang__.co_value, idmap)
File "./mindmap.kv", line 14, in <module>
on_release: app.save()
File "main.py", line 27, in save
pickle.dump(vertices, output)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1370, in dump
Pickler(file, protocol).dump(obj)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
self.save(obj)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 600, in save_list
self._batch_appends(iter(obj))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 615, in _batch_appends
save(x)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 306, in save
rv = reduce(self.proto)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 71, in _reduce_ex
state = base(self)
TypeError: __init__() takes exactly 0 positional arguments (1 given)
如果两个数组都是空的,我就不会遇到这个错误。当上面的情况发生时,edges
是空的,而 vertices
只有一个元素。我真的不知道该如何理解这个错误,因此也不知道该怎么解决。
这是我的 Vertex
类的 __init__
方法:
def __init__(self, pos=None, size=None):
super(Vertex, self).__init__()
self.pos = pos
self.size = size
if self.height < self.MINIMUM_HEIGHT + self.TEXT_OFFSET:
self.height = self.MINIMUM_HEIGHT + self.TEXT_OFFSET
if self.width < self.MINIMUM_WIDTH + self.TEXT_OFFSET:
self.width = self.MINIMUM_WIDTH + self.TEXT_OFFSET
self.originalSize = self.size
self.originalWidth = self.size[0]
self.newIdea = TextInput(
height = self.MINIMUM_HEIGHT,
size_hint = (None, None),
pos_hint = {'center_x':.5, 'center_y':.5},
background_active = '',
background_normal = '',
use_bubble = True,
foreground_color = (.4, .4, .4, 1),
cursor_color = (.4, .4, .4, 1))
self.newIdea.bind(text=self.on_text)
self.idea = Label(
height = self.MINIMUM_HEIGHT,
size_hint = (None, None),
pos_hint = {'center_x':.5, 'center_y':.5},
color = (.4, .4, .4,1))
Logger.info('Vertex: created')
1 个回答
4
确实,Kivy的EventDispatcher
对象在这里出了问题;object.__reduce_ex__
方法会寻找第一个不是自定义类的基类(也就是说,它没有设置Py_TPFLAGS_HEAPTYPE
这个标志),然后调用那个类的__init__
方法。这会失败,因为那个基类的__init__
方法不支持传入一个参数。
解决这个问题的方法是使用最新的pickle协议;它使用了不同的代码路径:
pickle.dump(edges, output, pickle.HIGHEST_PROTOCOL)
pickle.dump(vertices, output, pickle.HIGHEST_PROTOCOL)
因为最新的pickle协议(2)对新式类的支持更加高效。
考虑到Kivy对象层次结构的复杂性,你可能需要自定义序列化的过程。
我建议你可以实现__getnewargs__
方法,比如:
def __getnewargs__(self):
return (self.pos, self.size)
这样可以确保仅仅是那些数据被序列化,别的都不需要。在反序列化时,这两个值会被用来创建新的Vertex
对象。请注意,要使用这个方法,你必须使用最新的协议!
你也可以实现__getstate__
方法;如果你返回一个字典,就不需要对应的__setstate__
方法,但这可以让你初始化更多的属性。
如果这两个方法都没有实现,那么实例的__dict__
内容会被序列化。确保里面的所有内容都可以被序列化。