我正在尝试了解使用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
我们在此处看不到任何
__iadd__
操作被调用,这是有道理的,因为左侧表达式d["a"]
返回的整数不实现__iadd__
方法。我们确实看到python神奇地将+=
运算符转换为__getitem__
和__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__
方法。好像+=
运算符实际上已被使用两次:__getitem__
和__setitem__
会调用__iadd__
。 我需要帮助的地方如下:
+=
运算符转换为__getitem__
和__setitem__
调用的确切技术机制是什么? +=
运算符使用了两次? python不会将语句转换为d["m"] = d["m"] + 1
(在这种情况下,我们不会看到调用__add__
而不是__iadd__
吗?)最佳答案
在第一个示例中,您没有将+=
运算符应用于字典。您将其应用于存储在d['a']
键中的值,这是一个完全不同的对象。
换句话说,Python将检索d['m']
(一个__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
但在全局 namespace 中没有多余的名称
m
。如果
mutable()
实例未存储在字典中,而是存储在全局 namespace 中,则会发生完全相同的事件序列,但直接在globals()
字典上发生,并且您将看不到__getitem__
和__setitem__
调用。这记录在augmented assignment reference documentation下:
其中
d['m']
是目标;在这里评估目标涉及__getitem__
,将结果分配回原始目标将调用__setitem__
。