如何使具有__getattr__正确选择的类? [英] How to make a class which has __getattr__ properly pickable?
问题描述
我以一种简单的方式扩展了dict
,以直接使用d.key
表示法而不是d['key']
来访问其值:
I extended dict
in a simple way to directly access it's values with the d.key
notation instead of d['key']
:
class ddict(dict):
def __getattr__(self, item):
return self[item]
def __setattr__(self, key, value):
self[key] = value
现在,当我尝试将其腌制时,它将调用__getattr__
来查找__getstate__
,这既不存在也不是必需的.用__setstate__
取消提取也会发生同样的情况:
Now when I try to pickle it, it will call __getattr__
to find __getstate__
, which is neither present nor necessary. The same will happen upon unpickling with __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
以便可以正确选择?
How do I have to modify the class ddict
in order to be properly pickable?
推荐答案
问题不是pickle
,而是您的__getattr__
方法通过引发KeyError
异常打破了预期的约定.您需要修复__getattr__
方法来引发AttributeError
异常:
The problem is not pickle
but that your __getattr__
method breaks the expected contract by raising KeyError
exceptions. You need to fix your __getattr__
method to raise AttributeError
exceptions instead:
def __getattr__(self, item):
try:
return self[item]
except KeyError:
raise AttributeError(item)
现在为pickle
提供了缺少__getstate__
自定义挂钩的预期信号.
Now pickle
is given the expected signal for a missing __getstate__
customisation hook.
来自 object.__getattr__
文档:>
此方法应返回(计算出的)属性值或引发
AttributeError
异常.
This method should return the (computed) attribute value or raise an
AttributeError
exception.
(强调粗体的人).
如果您坚持保留KeyError
,那么至少您需要跳过以双下划线开头和结尾的名称,并为这些名称加一个AttributeError
:
If you insist on keeping the KeyError
, then at the very least you need to skip names that start and end with double underscores and raise an AttributeError
just for those:
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()
子类空的__slots__
元组;您无需在实例上使用额外的__dict__
属性映射,因为您可以将属性转移到键值对.这样可以为每个实例节省大量内存.
Note that you probably want to give your ddict()
subclass an empty __slots__
tuple; you don't need the extra __dict__
attribute mapping on your instances, since you are diverting attributes to key-value pairs instead. That saves you a nice chunk of memory per instance.
演示:
>>> 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'}
That pickle
tests for the __getstate__
method on the instance rather than on the class as is the norm for special methods, is a discussion for another day.
这篇关于如何使具有__getattr__正确选择的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!