如何使具有__getattr__正确选择的类? [英] How to make a class which has __getattr__ properly pickable?

查看:117
本文介绍了如何使具有__getattr__正确选择的类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我以一种简单的方式扩展了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'}

pickle在实例上测试方法 而不是在类上测试

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆