Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bcf06d3

Browse files
committed
#17973: Add FAQ entry for ([],)[0] += [1] both extending and raising.
This has come up often enough now on the tracker that it deserves a FAQ entry.
1 parent 86aecc3 commit bcf06d3

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

Doc/faq/programming.rst

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,89 @@ Use a list comprehension::
11031103
result = [obj.method() for obj in mylist]
11041104

11051105

1106+
Why does a_tuple[i] += ['item'] raise an exception when the addition works?
1107+
---------------------------------------------------------------------------
1108+
1109+
This is because of a combination of the fact that augmented assignment
1110+
operators are *assignment* operators, and the difference between mutable and
1111+
immutable objects in Python.
1112+
1113+
This discussion applies in general when augmented assignment operators are
1114+
applied to elements of a tuple that point to mutable objects, but we'll use
1115+
a ``list`` and ``+=`` as our exemplar.
1116+
1117+
If you wrote::
1118+
1119+
>>> a_tuple = (1, 2)
1120+
>>> a_tuple[0] += 1
1121+
Traceback (most recent call last):
1122+
...
1123+
TypeError: 'tuple' object does not support item assignment
1124+
1125+
The reason for the exception should be immediately clear: ``1`` is added to the
1126+
object ``a_tuple[0]`` points to (``1``), producing the result object, ``2``,
1127+
but when we attempt to assign the result of the computation, ``2``, to element
1128+
``0`` of the tuple, we get an error because we can't change what an element of
1129+
a tuple points to.
1130+
1131+
Under the covers, what this augmented assignment statement is doing is
1132+
approximately this::
1133+
1134+
>>> result = a_tuple[0].__iadd__(1)
1135+
>>> a_tuple[0] = result
1136+
Traceback (most recent call last):
1137+
...
1138+
TypeError: 'tuple' object does not support item assignment
1139+
1140+
It is the assignment part of the operation that produces the error, since a
1141+
tuple is immutable.
1142+
1143+
When you write something like::
1144+
1145+
>>> a_tuple = (['foo'], 'bar')
1146+
>>> a_tuple[0] += ['item']
1147+
Traceback (most recent call last):
1148+
...
1149+
TypeError: 'tuple' object does not support item assignment
1150+
1151+
The exception is a bit more surprising, and even more surprising is the fact
1152+
that even though there was an error, the append worked::
1153+
1154+
>>> a_tuple[0]
1155+
['foo', 'item']
1156+
1157+
To see why this happens, you need to know that for lists, ``__iadd__`` is equivalent
1158+
to calling ``extend`` on the list and returning the list. That's why we say
1159+
that for lists, ``+=`` is a "shorthand" for ``list.extend``::
1160+
1161+
>>> a_list = []
1162+
>>> a_list += [1]
1163+
>>> a_list
1164+
[1]
1165+
1166+
is equivalent to::
1167+
1168+
>>> result = a_list.__iadd__([1])
1169+
>>> a_list = result
1170+
1171+
The object pointed to by a_list has been mutated, and the pointer to the
1172+
mutated object is assigned back to ``a_list``. The end result of the
1173+
assignment is a no-op, since it is a pointer to the same object that ``a_list``
1174+
was previously pointing to, but the assignment still happens.
1175+
1176+
Thus, in our tuple example what is happening is equivalent to::
1177+
1178+
>>> result = a_tuple[0].__iadd__(['item'])
1179+
>>> a_tuple[0] = result
1180+
Traceback (most recent call last):
1181+
...
1182+
TypeError: 'tuple' object does not support item assignment
1183+
1184+
The ``__iadd__`` succeeds, and thus the list is extended, but even though
1185+
``result`` points to the same object that ``a_tuple[0]`` already points to,
1186+
that final assignment still results in an error, because tuples are immutable.
1187+
1188+
11061189
Dictionaries
11071190
============
11081191

0 commit comments

Comments
 (0)