6

を使用して python 辞書を更新する背後にある正確なメカニズムを理解しようとしていd[key] += diffます。マジック メソッドの呼び出しをトレースするためのヘルパー クラスがいくつかあります。

class sdict(dict):
    def __setitem__(self, *args, **kargs):
        print "sdict.__setitem__"
        return super(sdict, self).__setitem__(*args, **kargs)
    def __delitem__(self, *args, **kargs):
        print "sdict.__delitem__"
        return super(sdict, self).__delitem__(*args, **kargs)
    def __getitem__(self, *args, **kargs):
        print "sdict.__getitem__"
        return super(sdict, self).__getitem__(*args, **kargs)
    def __iadd__(self, *args, **kargs):
        print "sdict.__iadd__"
        return super(sdict, self).__iadd__(*args, **kargs)
    def __add__(self, *args, **kargs):
        print "sdict.__add__"
        return super(sdict, self).__add__(*args, **kargs)

class mutable(object):
    def __init__(self, val=0):
        self.value = val
    def __iadd__(self, val):
        print "mutable.__iadd__"
        self.value = self.value + val
        return self
    def __add__(self, val):
        print "mutable.__add__"
        return mutable(self.value + val)

これらのツールを使用して、ダイビングに行きましょう。

>>> d = sdict()
>>> d["a"] = 0
sdict.__setitem__
>>> d["a"] += 1
sdict.__getitem__
sdict.__setitem__
>>> d["a"]
sdict.__getitem__
1

__iadd__左側の式がメソッドd["a"]を実装しない整数を返すため、ここでは操作が呼び出されていません__iadd__。Python が魔法のように+=演算子を__getitem__and__setitem__呼び出しに変換しているのを確認できます。

続き:

>>> d["m"] = mutable()
sdict.__setitem__
>>> d["m"] += 1
sdict.__getitem__
mutable.__iadd__
sdict.__setitem__
>>> d["m"]
sdict.__getitem__
<__main__.mutable object at 0x106c4b710>

ここで、+=オペレーターはメソッドを正常に呼び出します__iadd__+=演算子が実際に 2 回使用されているようです。

  • への魔法の翻訳のための 1 回__getitem____setitem__呼び出し
  • 2回目の__iadd__電話です。

助けが必要な場所は次のとおりです。

  • +=演算子を__getitem__and__setitem__呼び出しに変換するための正確な技術的メカニズムは何ですか?
  • 2 番目の例で、+=演算子が 2 回使用されているのはなぜですか? Python はステートメントを次のように変換しませんかd["m"] = d["m"] + 1 (その場合__add__、代わりに呼び出されることはありませ__iadd__んか?)
4

2 に答える 2

11

+=最初の例では、演算子をディクショナリに適用しませんでした。キーに格納されている値に適用しましたが、それはd['a']まったく別のオブジェクトです。

d['m']つまり、Python は(__getitem__呼び出し)を取得+=し、それに演算子を適用してから、その式の結果をd['m'](__setitem__呼び出し) に戻します。

__iadd__メソッドはそのself場で変更されたオブジェクト、または新しいオブジェクトを返しますが、Python はメソッドが何を返したかを確実に知ることができません。したがって、常に呼び出す必要があります。d.__setitem__('m', <return_value_from_d['m'].__iadd__(1)>)

あなたがした場合、まったく同じことが起こります:

m = d['m']
m += 1
d['m'] = m

mただし、グローバル名前空間に余分な名前はありません。

mutable()インスタンスがディクショナリではなくグローバル名前空間に格納されている場合、まったく同じ一連のイベントが発生しますが、ディクショナリで直接発生し、との呼び出しはglobals()表示されません。__getitem____setitem__

これは、拡張された割り当てのリファレンス ドキュメントに記載されています。

拡張代入は、ターゲット (通常の代入ステートメントとは異なり、アンパックにはなりません) と式リストを評価し、2 つのオペランドで代入のタイプに固有のバイナリ演算を実行し、結果を元のターゲットに代入します。

d['m']ターゲットはどこですか。ここでのターゲットの評価には__getitem__、結果を元のターゲット呼び出しに割り当てることが含まれます__setitem__

于 2014-03-21T21:40:58.937 に答える
0

docsで指定されている__iadd__ように、操作をその場で実行する可能性がありますが、結果は自分自身または新しいオブジェクトのいずれかになるため、__setitem__呼び出されます。

于 2014-03-21T21:41:49.597 に答える