2011-03-03 4 views
4

カスタム動作を追加するために辞書から継承するクラスがあります。この場合、検証のために各キーと値が関数に渡されます。以下の例では、「検証」は単にメッセージを出力します。カスタムdictクラスを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

本当に必要な場合は、 '__new__'以外の' __dict__'を置き換えるべきではないと思います。ここでやろうとしていることは、カスタムの '__setattribute__'メソッドで行うべきです。 –

+0

正直言って、私は実際に何かをしようとしていません。検証用のdictクラスを書いていましたが、楽しみのために '__dict__'インスタンスとして試してみたかったのです。私の質問の理由は、なぜそれがこのように振る舞うのか、それを回避することができるのかどうか(そして、どのように)理解できるのかということです。 – porgarmingduod

答えて

5

これは最適化です。 __dict__のメタメソッドをサポートするためには、1回のインスタンス割り当てごとにメタメソッドの存在をチェックする必要があります。これは基本的な操作です(すべての属性の検索と割り当て)。これを確認するために必要な余分なカップルブランチは、言語全体のオーバーヘッドになります。obj.__getattr__obj.__setattr__で多かれ少なかれ冗長なものがあります。

+0

だから、__dict__インスタンスのメタメソッドは絶対にうまくいきません。私はあなたの知識のある答え(そしてあなたの評判)から、あなたが話していることを基本的に正確に知っていると思います。 – porgarmingduod

+0

'PyObject_GenericSetAttr'のオブジェクト/ object.cにハードコードされています:オブジェクトに' __dict__'がある場合は、 'PyDict_SetItem'を呼び出します。これはメタメソッドを呼び出さずに辞書値を直接設定します。私はそれが 'dict'のサブクラスではないものに' __dict__'を設定することが許されない理由だと思います。たとえば、 'x .__ dict__ = []'を試してみると、例外がスローされます。 –

+3

私はそれをPythonのドキュメントで明示的に言及していないバグと呼んでいます。 * class * docs(クラスインスタンス*ではない)は、特に 'CxがC .__ dict __ [" x "]'に変換されていると言うので、人々がそれを見て、クラスインスタンスに対して同じことが容易であると仮定するので、より明確になり得る。 http://docs.python.org/reference/datamodel.html –

関連する問題