2017-02-16 87 views
1

我以简单的方式直接访问它与d.key符号值,而不是d['key']扩展dict如何创建一个具有__getattr__适当选择的类?

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为了得到妥善拣选?

回答

3

问题不在于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试验上的实例而不是班级为is the norm for special methods__getstate__方法,是另一天的讨论。

-3

首先,我想你可能需要区分实例属性和类属性。 在Python公文章11.1.4关于酸洗,它说:这样的类,其字典或致电,有getstate(的结果)是picklable的

实例(参见有关详细信息,咸菜协议)。

因此,你得到的错误信息是当你试图腌制一个类的实例,但不是类本身 - 事实上,你的类定义将会很好。

现在为了清理类的一个对象,问题是你需要首先调用父类的序列化实现来正确设置事物。正确的代码是:

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.' 
+2

对不起,但这个答案中的一切都是错误的。 Pickle试图访问实例上的'__getstate__'方法,此时查询'__getattr__'方法。这会尝试在映射中通过该名称访问一个关键字,并且该关键字通过'KeyError'失败;这不是属性访问破坏pickle的正常例外。 –

+0

接下来,'dict'上没有'__getattr__'方法,所以'super()'调用将失败。该方法是可选的,只用作缺失属性的回退钩子。 –

+0

接下来,截取'__setattr__'的全部重点是覆盖正常的属性赋值过程,而不是重定向到字典键赋值。调用'super()'版本会失败这个目的。 –

相关问题