2011-03-03 29 views
4

我有一個類從字典繼承,以添加一些自定義行爲 - 在這種情況下,它將每個鍵和值傳遞給函數進行驗證。在下面的例子中,'驗證'只是打印一條消息。奇怪的行爲使用自定義的類類作爲Python類的__dict__屬性

字典的賦值按預期工作,只要將項添加到字典中就打印消息。但是,當我嘗試使用自定義詞典類型作爲類的__dict__屬性時,屬性賦值反過來將鍵/值放入我的自定義詞典類中,以某種方式設法將值插入到詞典中,同時完全繞過__setitem__(和另一個我定義的方法可能會添加鍵)。

自定義詞典:

from collections import MutableMapping 
class ValidatedDict(dict): 
    """A dictionary that passes each value it ends up storing through 
    a given validator function. 
    """ 
    def __init__(self, validator, *args, **kwargs): 
     self.__validator = validator 
     self.update(*args, **kwargs) 
    def __setitem__(self, key, value): 
     self.__validator(value) 
     self.__validator(key) 
     dict.__setitem__(self, key, value) 
    def copy(self): pass # snipped 
    def fromkeys(validator, seq, v = None): pass # snipped 
    setdefault = MutableMapping.setdefault 
    update = MutableMapping.update 

def Validator(i): print "Validating:", i 

使用它作爲一類收益率的行爲,我不明白__dict__屬性。

>>> d = ValidatedDict(Validator) 
>>> d["key"] = "value" 
Validating: value 
Validating: key 
>>> class Foo(object): pass 
... 
>>> foo = Foo() 
>>> foo.__dict__ = ValidatedDict(Validator) 
>>> type(foo.__dict__) 
<class '__main__.ValidatedDict'> 
>>> foo.bar = 100 # Yields no message! 
>>> foo.__dict__['odd'] = 99 
Validating: 99 
Validating: odd 
>>> foo.__dict__ 
{'odd': 99, 'bar': 100} 

有人可以解釋爲什麼它不按我期望的方式行事嗎?它可以或不可以按我嘗試的方式工作嗎?

+0

我不認爲你應該永遠不要替換'__dict__',除了'__new__' - 如果你真的必須的話。你想在這裏做什麼應該用自定義的'__setattribute__'方法來完成。 –

+0

說實話,我並不是真的「想做點什麼」。我正在寫驗證的字典類,只是爲了好玩,想把它作爲'__dict__'實例來嘗試。我的問題的原因是我真的很想理解它爲什麼會這樣,以及是否可以繞過它(以及如何)。 – porgarmingduod

回答

5

這是一個優化。爲了支持__dict__上的metamethods,每個實例分配都需要檢查metamethod的存在。這是一個基本的操作 - 每個屬性查找和分配 - 因此,需要額外的情侶分支來檢查這將成爲整個語言的開銷,對於obj.__getattr__obj.__setattr__或多或少多餘的東西。

+0

所以,答案顯然是'__dict__'實例上的metamethods絕對不行?我可以從你的知識豐富的答案(和你的名聲)中假設你基本上知道你在說什麼,並且我可以接受這個答案,因爲所有關於這個問題的事情都要講述嗎? – porgarmingduod

+0

它在'PyObject_GenericSetAttr'的Objects/object.c中被硬編碼:如果對象有一個'__dict__',它會調用'PyDict_SetItem',它直接設置字典值而不調用任何元方法。我認爲這也是爲什麼你不能將'__dict__'設置爲不是'dict'子類的東西;例如,如果你嘗試'x .__ dict__ = []',它會拋出異常。 –

+3

雖然我會把它稱爲一個Python文檔沒有明確提到的錯誤。 * class * docs(不是* class instance *)明確地說''Cx被翻譯爲C .__ dict __ [「x」]',人們很容易看到它,並且假設類實例也是這樣,所以這個可能會更清楚。 http://docs.python.org/reference/datamodel.html –