我以一种简单的方式扩展dict
,使用d.key
表示法而非d['key']
直接访问其值:
class ddict(dict):
def __getattr__(self, item):
return self[item]
def __setattr__(self, key, value):
self[key] = value
现在,当我尝试挑选它时,它会调用__getattr__
来查找__getstate__
,这既不存在也不必要。在使用__setstate__
>>> import pickle
>>> class ddict(dict):
... def __getattr__(self, item):
... return self[item]
... def __setattr__(self, key, value):
... self[key] = value
...
>>> pickle.dumps(ddict())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __getattr__
KeyError: '__getstate__'
如何修改课程ddict
才能正确挑选?
答案 0 :(得分:4)
问题不是pickle
,而是__getattr__
方法通过提出KeyError
例外来破解预期合同。您需要修改__getattr__
方法以引发AttributeError
例外:
def __getattr__(self, item):
try:
return self[item]
except KeyError:
raise AttributeError(item)
现在为pickle
提供了缺少__getstate__
自定义挂钩的预期信号。
来自object.__getattr__
documentation:
此方法应返回(计算)属性值或引发
AttributeError
异常。
(大胆强调我的)。
如果您坚持保留KeyError
,那么至少您需要跳过以双下划线开头和结尾的名称,并为这些名称提出AttributeError
:
def __getattr__(self, item):
if isinstance(item, str) and item[:2] == item[-2:] == '__':
# skip non-existing dunder method lookups
raise AttributeError(item)
return self[item]
请注意,您可能想要提供ddict()
子类an empty __slots__
tuple;您不需要在实例上使用额外的__dict__
属性映射,因为您将属性转移到键值对。这为每个实例节省了大量的内存。
演示:
>>> import pickle
>>> class ddict(dict):
... __slots__ = ()
... def __getattr__(self, item):
... try:
... return self[item]
... except KeyError:
... raise AttributeError(item)
... def __setattr__(self, key, value):
... self[key] = value
...
>>> pickle.dumps(ddict())
b'\x80\x03c__main__\nddict\nq\x00)\x81q\x01.'
>>> type(pickle.loads(pickle.dumps(ddict())))
<class '__main__.ddict'>
>>> d = ddict()
>>> d.foo = 'bar'
>>> d.foo
'bar'
>>> pickle.loads(pickle.dumps(d))
{'foo': 'bar'}
对pickle
方法上的__getstate__
方法进行<div class="container vertical-center" style="background: url('background.png')";>
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">LOGIN</div>
<div class="panel-body">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">
<span class="glyphicon glyphicon-user"></span>
</span>
<input type="text" class="form-control" placeholder="Username" />
</div>
<br />
<div class="input-group">
<span class="input-group-addon" id="basic-addon2">
<span class="glyphicon glyphicon-lock"></span>
</span>
<input type="text" class="form-control" placeholder="Password" />
</div>
</div>
<div class="text-center">
<button type="button" class="btn btn-primary btn-sm" style="align-content: center">Submit</button>
</div>
<br />
<div class="panel-footer">Trademark</div>
</div>
</div>
</div>
测试,而不是作为is the norm for special methods对该类进行测试,这是另一天的讨论。
答案 1 :(得分:-3)
首先,我认为您可能需要区分实例属性和类属性。 在Python官方文档第11.1.4章中关于酸洗,它说:
此类的实例 dict 或调用 getstate ()的结果是可选的(有关详细信息,请参阅pickle协议一节)。
因此,你得到的错误信息是当你试图挑选一个类的实例时,而不是类本身 - 实际上,你的类定义只会很好。
现在为了挑选类的对象,问题是你需要首先调用父类的序列化实现来正确设置。正确的代码是:
In [1]: import pickle
In [2]: class ddict(dict):
...:
...: def __getattr__(self, item):
...: super.__getattr__(self, item)
...: return self[item]
...:
...: def __setattr__(self, key, value):
...: super.__setattr__(self, key, value)
...: self[key] = value
...:
In [3]: d = ddict()
In [4]: d.name = "Sam"
In [5]: d
Out[5]: {'name': 'Sam'}
In [6]: pickle.dumps(d)
Out[6]: b'\x80\x03c__main__\nddict\nq\x00)\x81q\x01X\x04\x00\x00\x00nameq\x02X\x03\x00\x00\x00Samq\x03s}q\x04h\x02h\x03sb.'