2013-03-09 8 views
6

私はデータファイルを表すためにOrderedDict(Cpython、2.7.3)をサブクラス化しています。 __getitem__は、フィールドをデータファイルから取り出し、以下に投稿したコードと同様に現在のインスタンスに設定します。フィールドがディクショナリまたはディスク上のファイルにある場合は、いずれかの方法で読み取ることができるので、__contains__を無効にして、Trueを返します。しかし、これは鍵を調べるOrderedDictの能力を壊すようです。あなたは上記のコードでdictclass = dictを変更した場合、まだ動作しているようこと__contains__をオーバーライドすると、なぜOrderedDict.keysが壊れますか?

barbar 
[] 

ノート(次の出力を与える):あなたは上記のコードを実行した場合

from collections import OrderedDict 

dictclass = OrderedDict 

class Foo(dictclass): 
    def __getitem__(self,key): 
     try: 
      return dictclass.__getitem__(self,key) 
     except KeyError: 
      pass 

     data = key*2 
     self[key] = data 
     return data 

    def __contains__(self,whatever): 
     return dictclass.__contains__(self,whatever) or 'bar' in whatever 

a = Foo() 
print a['bar'] 
print a.keys() 

、あなたはこの出力を取得します。

barbar 
['bar'] 

何かひどく間違っていますか?

+0

私は[ソース](http://hg.python.org/cpython/file/2.7/Lib/collections.py)を読んでいますが、私はまだこれを考え出すのに苦労しています... – mgilson

+0

私はそうしています、あなたの問題はどこにあると思いますか: '__setitem__'と' __iter__'を見てください。 –

+0

@A.Rodas - はい、それは私が探していた場所です。多分私はあまりにも疲れているかもしれませんが、私はすべての論理を真っ直ぐ保つのに苦労していました。 – mgilson

答えて

6

Foo.__contains__に定義されていない。

a['bar'] 

self[key] = data 

これは、このように定義されOrderedDict.__setitem__、呼び出し実行コールFoo.__getitem__:012ため

def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 
    'od.__setitem__(i, y) <==> od[i]=y' 
    # Setting a new item creates a new link at the end of the linked list, 
    # and the inherited dictionary is updated with the new key/value pair. 
    if key not in self: 
     root = self.__root 
     last = root[PREV] 
     last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] 
    dict_setitem(self, key, value) 

をは定義されていません。

if key not in self: 

はTrueです。したがって、キーはself.__rootself.__mapに正しく追加されます。

Falseの場合Foo.__contains__

if key not in self: 

を定義しています。したがって、鍵はself.__rootself.__mapに正しく追加されていません。 Foo.__contains__有効な愚かなOrderedDict.__setitem__は、'bar'というキーが既に追加されていると考えています。


は、私は次のコード(__setitem____iter__にprint文を追加すること)で再生することが役に立ったと評価して:あなたはFooのサブクラスを作ることによって、この問題を回避することができ

from collections import OrderedDict 

dictclass = OrderedDict 

class Foo(dictclass): 
    def __getitem__(self,key): 
     try: 
      return dictclass.__getitem__(self,key) 
     except KeyError: 
      pass 

     data = key*2 
     self[key] = data 
     return data 

    def __contains__(self,whatever): 
     print('contains: {}'.format(whatever)) 
     return dictclass.__contains__(self,whatever) or 'bar' in whatever 

    def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 
     'od.__setitem__(i, y) <==> od[i]=y' 
     # Setting a new item creates a new link at the end of the linked list, 
     # and the inherited dictionary is updated with the new key/value pair. 
     print('key not in self: {}'.format(key not in self)) 
     if key not in self: 
      root = self._OrderedDict__root 
      last = root[PREV] 
      last[NEXT] = root[PREV] = self._OrderedDict__map[key] = [last, root, key] 
     dict_setitem(self, key, value) 

    def __iter__(self): 
     'od.__iter__() <==> iter(od)' 
     # Traverse the linked list in order. 
     NEXT, KEY = 1, 2 

     root = self._OrderedDict__root 
     curr = root[NEXT] 
     print('curr: {}'.format(curr)) 
     print('root: {}'.format(root)) 
     print('curr is not root: {}'.format(curr is not root)) 

     while curr is not root: 
      yield curr[KEY] 
      curr = curr[NEXT] 

a = Foo() 
print a['bar'] 
# barbar 

print a.keys() 
# ['bar'] 

お知らせcollections.MutableMappingを使用し、ほとんどの動作をOrderedDict属性に委譲します。

import collections 
dictclass = collections.OrderedDict 

class Foo(collections.MutableMapping): 
    def __init__(self, *args, **kwargs): 
     self._data = dictclass(*args, **kwargs) 
    def __setitem__(self, key, value): 
     self._data[key] = value 
    def __delitem__(self, key): 
     del self._data[key] 
    def __iter__(self): 
     return iter(self._data) 
    def __len__(self): 
     return len(self._data) 

    def __getitem__(self,key): 
     try: 
      return self._data[key] 
     except KeyError: 
      pass 

     data = key*2 
     self[key] = data 
     return data 

    def __contains__(self,whatever): 
     return dictclass.__contains__(self,whatever) or 'bar' in whatever 
でも定義され __contains__

a = Foo() 
print a['bar'] 
# barbar 

print a.keys() 
# ['bar'] 

を生み出す

+0

ありがとうございます。それはそうです - 私は 'self .__ root'に焦点を合わせるのに時間をかけすぎていました。どのように初期化されるのですか? - Thinking - ' self .__ root = root = []; root [:] = [root、root、None] '何が起こっているのですか? :X – mgilson

+0

私の考え方は非常に低いです - 一般的には多くの印刷文で構成されています。 :) – unutbu

2

あなたのコードが壊れているものはor 'bar' in whateverです。それを削除すると、あなたが言及した変更dictclass = dictのように動作します。 OrderedDict

__setitem__実装はこれです:self["bar"] = "barbar"

def __setitem__(self, key, value, dict_setitem=dict.__setitem__): 
    'od.__setitem__(i, y) <==> od[i]=y' 
    # Setting a new item creates a new link at the end of the linked list, 
    # and the inherited dictionary is updated with the new key/value pair. 
    if key not in self: 
     root = self.__root 
     last = root[0] 
     last[1] = root[0] = self.__map[key] = [last, root, key] 
    return dict_setitem(self, key, value) 

ので、条件がFalseにする必要がありますが、それでも任意の項目を挿入する前に真です。したがって、キーISN」はOrderedDict.__iter__に使用されるself.__rootに追加:

def __iter__(self): 
    'od.__iter__() <==> iter(od)' 
    # Traverse the linked list in order. 
    root = self.__root 
    curr = root[1]         # start at the first node 
    while curr is not root: 
     yield curr[2]        # yield the curr[KEY] 
     curr = curr[1]        # move to next node 

値を取得するためのコードは、反復子を使用しself.__root"bar"が含まれていないので、この具体的なキーの値に戻すことができません。

+0

ねえ、これです。ありがとう。 +1。もちろん、私のコードでは、 '' bar 'は何でも ''私が削除したくないもっと複雑なものです。 OrderedDictを適切に動作させるためのハッキングが難しいと思う。私は普通の辞書をサブクラス化し、別の '__order'リストを保持することにします。 – mgilson

+0

@mgilson: 'Foo' *は、* be-a * OrderedDictの代わりに' OrderedDict'を持っていますか? – unutbu

+0

@unutbu - 私はそれを解凍できるようにマッピング型にしたいです...私は自分自身で注文を追跡することができます。 – mgilson