Adding a dictionary to a list using the __iadd__
notation seems to add the keys of the dictionary as elements in the list. Why? For example
a = []
b = {'hello':'world'}
a += b
>> a now stores ['hello']
The documentation for plus-equals on collections doesn't imply to me that this should happen:
For instance, to execute the statement x += y, where x is an instance of a class that has an
__iadd__
() method,x.__iadd__(y)
is called. If x is an instance of a class that does not define a__iadd__()
method,x.__add__(y)
andy.__radd__(x)
are considered, as with the evaluation ofx + y
But, logically, both
a + b # TypeError Exception
and
b + a # TypeError Exception
Are not defined. Furthermore, b+=a
raises a TypeError too. I don't see any special implementation in the source that would explain things, but I'm not 100% sure where to look.
The closest question on SO I found is this one, asking about += on dictionaries, but that's just asking about a data structure with itself. This one had a promising title about list self-addition, but it claims "__add__
" is being applied under the hood, which shouldn't be defined between lists and dictionaries.
My best guess is that the __iadd__
is invoking extend, which is defined here, and then it tries to iterate over the dictionary, which in turn yields its keys. But this seems... weird? And I don't see any intuition of that coming from the docs.
My best guess is that the iadd is invoking extend, which is defined here, and then it tries to iterate over the dictionary, which in turn yields its keys. But this seems... weird? And I don't see any intuition of that coming from the docs.
This is the correct answer for why this happens. I've found the relevant docs that say this-
In the docs you can see that in fact __iadd__
is equivalent to .extend()
, and here it says:
list.extend(iterable): Extend the list by appending all the items from the iterable.
In the part about dicts it says:
Performing list(d) on a dictionary returns a list of all the keys used in the dictionary
So to summarize, a_list += a_dict
is equivalet to a_list.extend(iter(a_dict))
, which is equivalent to a_list.extend(a_dict.keys())
, which will extend the list with the list of keys in the dictionary.
We can maybe discuss on why this is the way things are, but I don't think we will find a clear-cut answer. I think +=
is a very useful shorthand for .extend
, and also that a dictionary should be iterable (personally I'd prefer it returning .items()
, but oh well)
Edit: You seem to be interested in the actual implementation of CPython, so here are some code pointers:
dict iterator returning keys:
static PyObject *
dict_iter(PyDictObject *dict)
{
return dictiter_new(dict, &PyDictIterKey_Type);
}
list.extend(iterable) calling iter() on its argument:
static PyObject *
list_extend(PyListObject *self, PyObject *iterable)
{
...
it = PyObject_GetIter(iterable);
...
}
+= being equivalent to list.extend():
static PyObject *
list_inplace_concat(PyListObject *self, PyObject *other)
{
...
result = list_extend(self, other);
...
}
and then this method seems to be referenced above inside a PySequenceMethods
struct, which seems to be an abstraction of sequences that defines common actions such as concatenating in-place, and concatenating normally (which is defined as list_concat
in the same file and you can see is not the same).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With