我如何取消列出' dict'的子类。用python3中的__setitem__验证?
我正在使用Python3.3。在2.x的Pickle协议中,这个问题可能不存在,但我实际上没有得到验证。
假设我已经创建了一个 dict
子类,该子类每次更新键时都计算。类似的内容:
class Foo(dict):
def __init__(self):
self.counter = 0
def __setitem__(self, key, value):
print(key, value, self.__dict__)
if key == 'bar':
self.counter += 1
super(Foo, self).__setitem__(key, value)
您可能会这样使用:
>>> f = Foo()
>>> assert f.counter == 0
>>> f['bar'] = 'baz'
... logging output...
>>> assert f.counter == 1
现在让我们腌制并取消挑剔:
>>> import pickle
>>> f_str = pickle.dumps(f)
>>> f_new = pickle.loads(f_str)
bar baz {}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 133, in __setitem__
self.counter += 1
AttributeError: 'Foo' object has no attribute 'counter'
我认为 print()
in __ setItem __ __
显示问题: pickle.loads
试图在之前编写字典的键 它写入对象的属性...至少我认为这是正在发生的事情。如果您删除 self.counter
参考 foo .__ setItem __()
:
>>> f_mod = ModifiedFoo()
>>> f_mod['bar'] = 'baz'
>>> f_mod_str = pickle.dumps(f_mod)
>>> f_mod_new = pickle.loads(f_mod_str)
bar baz {}
>>> assert f_mod_new.counter == 0
>>>
这只是Pickle协议的副产品吗?我已经尝试在 __ setState上进行变体__
让它正确地取消选择,但据我所知,它触发 __ setItem __
error, __ setState __ setstate __
is甚至叫。有什么办法可以修改此对象以允许取消选择?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如
pickle
文档:在您的情况下,您 do 想调用
__ INIT __
。但是,由于您的课程是新样式的类,因此您不能使用__ getinitargs __
(无论如何,这在Python3中不支持)。您可以尝试编写自定义__ getState __
和__ setState __
方法:但是,此仍然不起作用,因为因为您是在子分类
dict,
和dict
有一个用于腌制的特殊处理程序,__ getState __
方法 it nes nath in ,但是__ setState __
方法不是。您可以围绕此定义
__降低__
方法:As stated by
pickle
documentation:In your case you do want to invoke
__init__
. However since your class is a new-style class you cannot use__getinitargs__
(which isn't supported in python3 anyway). You could try to write your custom__getstate__
and__setstate__
methods:However this still doesn't work, because since you are subclassing
dict
anddict
has a special handler for pickling, the__getstate__
method is called, however the__setstate__
method is not.You can work around this defining the
__reduce__
method:您正在子类
dict
,并且Pickle协议将使用专用dict
处理程序将键和值存储在生成的泡菜数据中,并使用A 不同的一组opcodes,再次将其还原到您的对象。结果,
__ setState __
仅在还原字典密钥后才被称为,并且状态仅包含counter
属性。这里有两个工作:
面对
__ INIT __ INIT __
未被调用:,使您的
计数器
代码有弹性。此处
计数器
是类属性,因此始终存在。您也可以使用:确保缺少属性有默认值。
提供
__ Newargs __
方法;它可以返回一个空的元组,但指定它可以确保__新__
在未卖时调用,又可以调用__ INT __ INT __
:请注意,在调用
__ INT __
之后,Unpickler仍然会设置所有键,然后 Restore__ dict __ dict __
。self.counter
最终会反映正确的值。演示:
第一方法:
第二种方法:
You are subclassing
dict
, and the pickle protocol will use the dedicateddict
handler to store the keys and values in the resulting pickle data, using a different set of opcodes to restore these to your object again.As a result,
__setstate__
is going only going to be called after restoring the dictionary keys, and the state contains only thecounter
attribute.There are two work-arounds here:
Make your
counter
code resilient in the face of__init__
not being called:Here
counter
is a class attribute and thus always present. You could also use:to ensure there is a default value for the missing attribute.
Provide a
__newargs__
method; it can return an empty tuple, but specifying it ensures that__new__
is called when unpickling, which in turn could call__init__
:Note that after
__init__
is called, the unpickler still will set all the keys, then restore__dict__
.self.counter
will reflect the correct value in the end.Demos:
1st approach:
2nd approach:
您可以通过添加a __降低__() 方法将用于获取参数以传递给用户定义的函数,以在对象未进行挑选时重新构造该函数。
虽然,由于您的班级是
dict
子类,但并不像我最初想象的那样琐碎的实现,但是一旦我弄清楚需要做什么,这很简单。这是我想到的 - 请注意,_foo_unpickle_helper()
函数不能是类的常规或静态方法,因此这就是为什么在模块级别定义的原因:输出:输出:
You can add pickle support to your dictionary subclass by adding a
__reduce__()
method which will be used to get arguments to pass to a user defined function to reconstitute the object when it's unpickled.Although, since your class is a
dict
subclass, not wasn't quite as trivial to implement as I originally thought, but it's fairly simple once I figured out what needed to be done. Here's what I came up with — note that the_Foo_unpickle_helper()
function can't be a regular or static method of the class, so that's why it's defined at the module level:Output: