我试图理解使用d[key] += diff
更新python字典背后的确切机制。我有一些helper类来跟踪魔法方法调用:
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__
?)
因为,正如在docs中指定的,
__iadd__
可能就地执行操作,但结果将是self或新对象,因此调用__setitem__
。在第一个示例中,没有将
+=
运算符应用于字典。您将它应用于存储在d['a']
键中的值,这是一个完全不同的对象。换句话说,Python将检索
d['m']
(一个__getitem__
调用),对其应用+=
运算符,然后将该表达式的结果设置回d['m']
(一个__setitem__
调用)。__iadd__
方法要么返回经过适当修改的self
,要么返回一个新对象,但Python无法确定返回的是什么。所以它总是要调用d.__setitem__('m', <return_value_from_d['m'].__iadd__(1)>)
。如果你这样做了,同样的事情也会发生:
但是在全局命名空间中没有额外的名称
m
。如果
mutable()
实例没有存储在字典中,而是存储在全局命名空间中,则会发生完全相同的事件序列,但直接存储在globals()
字典中,您将看不到__getitem__
和__setitem__
调用。这是在augmented assignment reference documentation下记录的:
其中
d['m']
是目标;在这里对目标求值涉及__getitem__
,将结果分配回原始目标调用__setitem__
。相关问题 更多 >
编程相关推荐