Cas*_*mon 6 python pickle python-3.x
我有自定义列表和字典类,在Python 3.7中进行unpickling时不再有效.
import pickle
class A(dict):
pass
class MyList(list):
def __init__(self, iterable=None, option=A):
self.option=option
if iterable:
for x in iterable:
self.append(x)
def append(self, obj):
if isinstance(obj, dict):
obj = self.option(obj)
super(MyList, self).append(obj)
def extend(self, iterable):
for item in iterable:
self.append(item)
if __name__ == '__main__':
pickle_file = 'test_pickle'
my_list = MyList([{'a': 1}])
pickle.dump(my_list, open(pickle_file, 'wb'))
loaded = pickle.load(open(pickle_file, 'rb'))
print(isinstance(loaded[0], A))
Run Code Online (Sandbox Code Playgroud)
适用于Python 2.6到3.6:
"C:\Program Files\Python36\python.exe" issue.py
True
Run Code Online (Sandbox Code Playgroud)
但是不再self.option
在3.7中正确设置.
"C:\Program Files\Python37\python.exe" issue.py
Traceback (most recent call last):
File "issue.py", line 28, in <module>
loaded = pickle.load(open(pickle_file, 'rb'))
File "issue.py", line 21, in extend
self.append(item)
File "issue.py", line 16, in append
obj = self.option(obj)
AttributeError: 'MyList' object has no attribute 'option'
Run Code Online (Sandbox Code Playgroud)
如果我要删除该extend
功能,它会按预期工作.
我也试过添加__setstate__
,但之前没有调用过,extend
所以option
在那一点上仍未定义.
我必须直接从dict
和继承list
,并且我需要覆盖我的代码中的append
和extend
函数.有没有办法option
预先设置或另一个修复?行为的这种变化是否记录在案并且是否合理?
感谢您的时间
取消列表对象从使用切换list.update()
到list.extend()
,因为对于某些list
子类来说这可能更快.
但是,通过这种更改,对列表对象测试的unpickling代码的方式也发生了变化
if (PyList_Check(list)) {
Run Code Online (Sandbox Code Playgroud)
至
if (PyList_CheckExact(list)) {
Run Code Online (Sandbox Code Playgroud)
这种变化会影响您的代码.上面的测试寻找一个快速路径,说如果我们有一个列表类,然后用于PyList_SetSlice()
加载数据,而不是在新实例上显式调用.extend()
or .append()
方法的较慢路径.旧版本(Python 3.6及更早版本)接受列表和子类,新版本只接受list
自身,而不是子类!
因此,对于Python 3.6及更早版本,在MyList.append()
调用自定义方法时不会调用.在Python 3.7,取储存您的自定义,当list
方法被调用.这是非常有意的,应允许子类提供MyList.extend()
在unpickling时调用的自定义方法.
解决方法很简单.在取消渲染时,您的数据已经被包装,您不需要重新应用该包装器.如果您没有.extend()
设置,只需跳过应用它:
def append(self, obj):
if isinstance(obj, dict):
try:
obj = self.option(obj)
except AttributeError:
# something's wrong, are we unpickling on Python 3.7 or newer?
if 'option' in self.__dict__:
# no, we are not, because 'option' has been set, this must
# be an error in the option() call, so re-raise
raise
# yes, we are, just ignore this, obj is already wrapped
super(MyList, self).append(obj)
Run Code Online (Sandbox Code Playgroud)
这一切都意味着您不能依赖已经恢复的任何实例属性.如果这是一个大问题(你仍然需要在unpickling时参考实例状态),那么你将不得不提供一个不同的self.option
方法,一个不返回数据作为结果元组的索引3中的迭代器.__reduce_ex__
对于协议版本2,3和4返回list().__reduce_ex__()
.
(copyreg.__newobj__, type(self), self.__dict__, iter(self), None)
例如,必须使用自定义版本.这确实带来了一些额外的开销((type(self), (tuple(self), self.option), None, None, None)
在酸洗和去除时会有额外的内存).
归档时间: |
|
查看次数: |
1056 次 |
最近记录: |