2012-11-07 15 views
13

ネストされた辞書で作業する方法を検索すると、次のコードがnoskloで投稿されていることがわかりました。Pythonでは、次のAutoVivificationクラスはどのように機能しますか?

class AutoVivification(dict): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item): 
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 

テスト:

a = AutoVivification() 

a[1][2][3] = 4 
a[1][3][3] = 5 
a[1][2]['test'] = 6 

print a 

出力:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}} 

私はかなり初心者プログラマです。私は、私の唯一の正式な訓練が高校のターボ・パスカルにあるということで、私が自分の時間に知っていることのほとんどを学んだ。 __init__、クラスメソッドを使用し、クラスのインスタンス内にデータを格納するなど、簡単な方法でクラスを使用できます。foo.man = 'choo'

どのようにして一連の角括弧がクラスを介して正しく指示されるのかわかりません(私は彼らが何とか__getitem__と呼んでいると推測しています)、メソッド3を呼び出すことなく、回。

私は、クラス宣言の​​が__init__によって処理されるという印象を受けました。

これまではtry: except:をかなり単純な方法で使用してきましたが、それはtryのように私には、それが実行されるとき、一連の機能を呼び出すように見える__getitem__。現在のレベルの辞書が存在する場合、試行は合格して次の辞書に移動します。 exceptは、KeyErrorがあるときに実行されますが、私はselfが以前と同じように使われているのを見ていません。 Selfは辞書のように扱われていますが、selfclass AutoVivificationのインスタンスでした...それはどちらですか?私はこのような行を二度も割り当てたことはありませんfoo = man = chooしかしvalueself[item]を指していると思われますが、self[item]type(self)の結果を指しています。しかしtype(self)は次のようなものを返すでしょう:<class '__main__.AutoVivification'>そうでしょうか?私は最後の余分な丸括弧が何のためにあるか分からない。関数がどのように呼び出されているのかわからないので、valueが返されている場所はわかりません。

すべてのご質問があります。私は理解していないと私はそれほど多くのことがありますが、私は非常にほとんど保持しない時間のためのドキュメントを読むことが不足しています。このコードは目的を果たすように見えますが、使用する前に理解したいと思います。

私は自分のプログラムでネストされた辞書を使って何をしようとしているのか知りたければ、天文学的なスケールで地図データを保持しようとしています。私は10^6項目の辞書/リストを4回入れ子にすることはできませんが(それは10^24項目です!)、空白はほとんど空ですので、空の値を完全に残して、そこに何かがあるときだけ割り当てることができます。私を困惑させていたのは、辞書を効率的に処理する方法でした。

答えて

18

ライン:

class AutoVivification(dict): 

我々はdictのサブクラスを作るので、AutoVivificationは、いくつかのローカルな変更とdictの一種です。

def __getitem__(self, item): 

__getitem()__ hook

は、誰かが [...]インデックス検索を通じてインスタンス上の項目にアクセスしようとするたびに呼び出されます。したがって、誰かが object[somekey]を実行するたびに、 type(object).__getitem__(object, somekey)が呼び出されます。

私たちは一瞬tryをスキップします、次の行は次のとおりです。

return dict.__getitem__(self, item) 

これは結合していない方法__getitem__()を呼び出し、一緒に鍵を使用して、そこに私たち自身のインスタンスに渡します。つまり、親クラスdictで定義されたオリジナル__getitem__と呼びます。

itemというキーが辞書にない場合は、KeyErrorが発生します。どこtry:これは、except KeyErrorコンボは入って来:

だから、
try: 
     return dict.__getitem__(self, item) 
    except KeyError: 
     value = self[item] = type(self)() 
     return value 

、(dictのサブタイプである)現在のインスタンスが指定したキーを持っていない場合、それはKeyError例外キャッチします元のdict.__getitem__()メソッドがスローする代わりに、という新しい値を作成し、に格納し、その値を返します。

selfdictのサブクラスなので、辞書です。したがって、新しい値(これには__setitem__ hookが偶然に使用されます)を割り当てることができます。この場合、と同じタイプの新しいインスタンスが作成されます。それは別のdictサブクラスです。

a[1][2][3] = 4に電話するとどうなりますか? Pythonはステップによって、このステップを経る:

  1. a[1]type(a).__getitem__(a, 1)につながります。カスタム__getitem__メソッドがAutoVivificationの場合、KeyErrorがキャッチされ、新しいインスタンスがAutoVivificationに作成され、キー1の下に保存され、返されます。

  2. a[1]は、空のAutoVivificationインスタンスを返しました。次のアイテムアクセス[2]がそのオブジェクトに対して呼び出され、ステップ1で何が起きたかを繰り返します。 KeyErrorがあり、新しいインスタンスAutoVivificationが作成され、2キーの下に格納され、その新しいインスタンスが呼び出し元に返されます。

  3. a[1][2]AutoVivificationインスタンスが返されました。次のアイテムアクセス[3]がそのオブジェクトに対して呼び出され、ステップ1(ステップ2)で何が起きたかを繰り返します。 KeyErrorがあり、新しいインスタンスAutoVivificationが作成され、3キーの下に格納され、その新しいインスタンスが呼び出し元に返されます。

  4. a[1][2][3]は、空のAutoVivificationインスタンスを返しました。ここで、そのインスタンスに新しい値、つまり4を保存します。

あなたは、コード、a[1][3][3] = 5のあなたの次の行に移動すると、トップレベルAutoVivificationインスタンスがすでに1鍵を持っている、とreturn dict.__getitem__(self, item)ラインで作成したAutoVivificationインスタンスであることを起こる、対応する値を返します上記のステップ1。

そこから、[3]項目アクセス・コールが再び新しいAutoVivificationインスタンスを作成します(a[1]のオブジェクトが2キーを持っているので)、そして私たちは再び、すべて同じ手順を経ます。

2

object.__getitem__のドキュメントを参照してください。

class AutoVivification(dict)宣言はdictAutoVivificationサブクラスを作るので、それが明示的にいくつかの動作をオーバーライドしない限り、同じことがdictとして希望した振る舞い - それは__getitem__をオーバーライドするときに、このクラスが行うように。(。少なくともPythonの2.xで、Pythonの3がより良い構文を持っている):

コールdict.__getitem__(self, item)には、通常、代わりのように書かれることになる

super(AutoVivification, self).__getitem__(item) 

いずれかの方法で、これが何をするか、デフォルトdictをさせる試みるありますビヘイビアは実行されますが、機能しない場合はフォールバックを実装します。

type(self)()は、最初にselfインスタンスに対応するクラスオブジェクトを検索し、クラスオブジェクトを呼び出します。この場合は、AutoVivification()と同じです。

あなたのためにそれをクリアする希望!ラインによって

関連する問題