2017-02-16 88 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()'版本會失敗這個目的。 –

相關問題