@@ -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+
11061189Dictionaries
11071190============
11081191
0 commit comments