Python字典“;加上“相等”;行为

Python字典“;加上“相等”;行为,python,dictionary,operators,magic-methods,mutators,Python,Dictionary,Operators,Magic Methods,Mutators,我试图理解使用d[key]+=diff更新python字典背后的确切机制。我有一些助手类来跟踪魔术方法调用: class sdict(dict): def __setitem__(self, *args, **kargs): print "sdict.__setitem__" return super(sdict, self).__setitem__(*args, **kargs) def __delitem__(self, *args, **ka

我试图理解使用
d[key]+=diff
更新python字典背后的确切机制。我有一些助手类来跟踪魔术方法调用:

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
我们看不到这里调用的任何
\uuuu iadd\uuuu
操作,这是有意义的,因为左侧表达式
d[“a”]
返回一个不实现
\uu iadd\uu
方法的整数。我们确实看到python神奇地将
+=
操作符转换为
\uuu getitem\uuuuu
\uuuu setitem\uuuu
调用

继续:

>>> d["m"] = mutable()
sdict.__setitem__
>>> d["m"] += 1
sdict.__getitem__
mutable.__iadd__
sdict.__setitem__
>>> d["m"]
sdict.__getitem__
<__main__.mutable object at 0x106c4b710>
>d[“m”]=mutable()
sdict.\uuuu设置项__
>>>d[“m”]+=1
sdict.\uu获取项目__
可变的__
sdict.\uuuu设置项__
>>>d[“m”]
sdict.\uu获取项目__
这里,
+=
操作符成功地调用了一个
\uuIdd\uIdd
方法。看起来
+=
运算符实际使用了两次:

  • 对于
    \uuuu getitem\uuuuuuu
    \uuuuuuuu setitem\uuuuuuuuuu
    调用的神奇转换
  • 第二次用于
    \uuuu iadd\uuu
    调用
我需要帮助的地方如下:

  • +=
    运算符转换为
    \uuu getitem\uuuuuuu
    \uuuu setitem\uuuuu
    调用的确切技术机制是什么
  • 在第二个示例中,为什么要使用
    +=
    运算符两次?python是否将语句转换为
    d[“m”]=d[“m”]+1
    (在这种情况下,我们不会看到调用
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

在第一个示例中,您没有将
+=
运算符应用于字典。您将其应用于存储在
d['a']
键中的值,这是一个完全不同的对象

换句话说,Python将检索
d['m']
(a
\uuuu getitem\uuuu
调用),对其应用
+=
操作符,然后将该表达式的结果设置回
d['m']
\uu setitem\uu
调用)

\uuuu iadd\uuu
方法要么返回原地变异的
self
,要么返回一个新对象,但Python无法确定该方法返回了什么。因此它必须始终调用
d.\uuu setitem('m')

如果你这样做了,同样的事情也会发生:

m = d['m']
m += 1
d['m'] = m
但是在全局命名空间中没有额外的名称
m

如果
mutable()

这记录在以下文件中:

扩充赋值计算目标(与普通赋值语句不同,目标不能是解包)和表达式列表,对两个操作数执行特定于赋值类型的二进制运算,并将结果赋值给原始目标


其中
d['m']
是目标;在这里评估目标涉及到
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

,因为,如中所述,
\uuuuuuuuuuuuuuuuuuuuuuuuuu,因此,调用了
\uuuu setitem\uuuuuu

我猜,由于“get the value of”操作符已经实现,因此该语句被认为等价于这样的语句:“X=get;X+=1;set(X);”这一切在我看来都非常简单<代码>sdict.\uuuu iadd\uuuu
从未被调用,因为您没有任何代码在
sdict
对象上执行
+=
。您只对
sdict
中的元素执行
+=
——在第一种情况下是整数,在第二种情况下是可变的
。但是您已经证明您理解这一点,所以我不确定问题是什么。我想我的困惑在于python将
+=
转换为
\uuu getitem\uuuuuuuuuu
\uuuuu setitem\uuuuuuu
调用字典的实际机制。在第二个示例中,它似乎使用了一个
+=
操作符两次:一次调用
\uuu getitem\uuuuuu
\uuuu setitem\uuuuu
,第二次调用
可变
上的
\uuu iadd\uuuu
。这是怎么回事?@moatra:你听了我回答的最后一部分了吗?为了使
+=
工作,Python需要首先检索等式左侧的一个对象(
\uuu getitem\uuu
),然后应用
+=
,然后在完成后,将对象放回它的来源(
\uu setitem\uuu
)。我目前的理解是Python将
d[“m”+=1
转换为
d[“m”=d[“m”]+1
。它是否将其转换为
d[“m”]=d[“m”]+=1
?不管在何处指定了此行为?您的理解是错误的。请参阅。如果
d['m']
上没有定义
\u iadd\ucode>方法,则将应用
d['m']=d['m']+1
,但这里的情况并非如此。