en_*_*ght 13 python dictionary list operator-overloading
使用__iadd__
符号将字典添加到列表似乎将字典的键添加为列表中的元素。为什么?例如
a = []
b = {'hello':'world'}
a += b
>> a now stores ['hello']
Run Code Online (Sandbox Code Playgroud)
集合上的plus-equals 文档对我来说并不意味着这应该发生:
例如,要执行语句 x += y,其中 x 是具有
__iadd__
() 方法的类的实例,x.__iadd__(y)
被调用。如果 x 是一个没有定义__iadd__()
方法的类的实例,x.__add__(y)
并且y.__radd__(x)
被考虑,就像评估x + y
但是,从逻辑上讲,两者
a + b # 类型错误异常
和
b + a # 类型错误异常
没有定义。此外,b+=a
也会引发 TypeError。我在源代码中没有看到任何可以解释事物的特殊实现,但我不能 100% 确定在哪里查看。
我发现的关于 SO 的最接近的问题是this one,询问字典上的 += ,但这只是询问自身的数据结构。这个有一个关于列表自添加的有希望的标题,但它声称“ __add__
”是在幕后应用的,不应该在列表和字典之间定义。
我最好的猜测是__iadd__
调用了在此处定义的扩展,然后它尝试迭代字典,从而产生它的键。但这似乎……很奇怪?而且我没有看到来自文档的任何直觉。
Tha*_*rdo 14
我最好的猜测是iadd正在调用这里定义的扩展,然后它尝试遍历字典,从而产生它的键。但这似乎……很奇怪?而且我没有看到来自文档的任何直觉。
这是为什么会发生这种情况的正确答案。我找到了相关的文档说这个 -
在文档中,您可以看到实际上__iadd__
等效于.extend()
,这里说:
list.extend(iterable):通过添加可迭代对象中的所有项目来扩展列表。
对字典执行 list(d) 返回字典中使用的所有键的列表
总而言之,a_list += a_dict
相当于a_list.extend(iter(a_dict))
,相当于a_list.extend(a_dict.keys())
,这将使用字典中的键列表扩展列表。
我们也许可以讨论为什么会是这样,但我认为我们不会找到明确的答案。我认为这+=
是一个非常有用的简写.extend
,而且字典应该是可迭代的(我个人更喜欢它返回.items()
,但是哦)
编辑:您似乎对 CPython 的实际实现感兴趣,所以这里有一些代码指针:
static PyObject *
dict_iter(PyDictObject *dict)
{
return dictiter_new(dict, &PyDictIterKey_Type);
}
Run Code Online (Sandbox Code Playgroud)
list.extend(iterable) 在其参数上调用 iter():
static PyObject *
list_extend(PyListObject *self, PyObject *iterable)
{
...
it = PyObject_GetIter(iterable);
...
}
Run Code Online (Sandbox Code Playgroud)
static PyObject *
list_inplace_concat(PyListObject *self, PyObject *other)
{
...
result = list_extend(self, other);
...
}
Run Code Online (Sandbox Code Playgroud)
然后这个方法似乎在上面引用了一个PySequenceMethods
结构体,它似乎是序列的抽象,定义了常见的操作,例如就地连接和正常连接(定义为list_concat
在同一个文件中,你可以看到不是相同)。