summaryrefslogtreecommitdiffstats
path: root/Doc/faq
diff options
context:
space:
mode:
authorR David Murray <rdmurray@bitdance.com>2013-05-20 14:32:46 (GMT)
committerR David Murray <rdmurray@bitdance.com>2013-05-20 14:32:46 (GMT)
commitbcf06d364df2b2a69dd419124b218f44284d43df (patch)
tree46ff38836d34f29c96760669d7fce58a94f0cec8 /Doc/faq
parent86aecc360f8fd635a91c5b7ffe63b1b9a3e17a31 (diff)
downloadcpython-bcf06d364df2b2a69dd419124b218f44284d43df.zip
cpython-bcf06d364df2b2a69dd419124b218f44284d43df.tar.gz
cpython-bcf06d364df2b2a69dd419124b218f44284d43df.tar.bz2
#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.
Diffstat (limited to 'Doc/faq')
-rw-r--r--Doc/faq/programming.rst83
1 files changed, 83 insertions, 0 deletions
diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst
index 0f4d148..7713450 100644
--- a/Doc/faq/programming.rst
+++ b/Doc/faq/programming.rst
@@ -1103,6 +1103,89 @@ Use a list comprehension::
result = [obj.method() for obj in mylist]
+Why does a_tuple[i] += ['item'] raise an exception when the addition works?
+---------------------------------------------------------------------------
+
+This is because of a combination of the fact that augmented assignment
+operators are *assignment* operators, and the difference between mutable and
+immutable objects in Python.
+
+This discussion applies in general when augmented assignment operators are
+applied to elements of a tuple that point to mutable objects, but we'll use
+a ``list`` and ``+=`` as our exemplar.
+
+If you wrote::
+
+ >>> a_tuple = (1, 2)
+ >>> a_tuple[0] += 1
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+The reason for the exception should be immediately clear: ``1`` is added to the
+object ``a_tuple[0]`` points to (``1``), producing the result object, ``2``,
+but when we attempt to assign the result of the computation, ``2``, to element
+``0`` of the tuple, we get an error because we can't change what an element of
+a tuple points to.
+
+Under the covers, what this augmented assignment statement is doing is
+approximately this::
+
+ >>> result = a_tuple[0].__iadd__(1)
+ >>> a_tuple[0] = result
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+It is the assignment part of the operation that produces the error, since a
+tuple is immutable.
+
+When you write something like::
+
+ >>> a_tuple = (['foo'], 'bar')
+ >>> a_tuple[0] += ['item']
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+The exception is a bit more surprising, and even more surprising is the fact
+that even though there was an error, the append worked::
+
+ >>> a_tuple[0]
+ ['foo', 'item']
+
+To see why this happens, you need to know that for lists, ``__iadd__`` is equivalent
+to calling ``extend`` on the list and returning the list. That's why we say
+that for lists, ``+=`` is a "shorthand" for ``list.extend``::
+
+ >>> a_list = []
+ >>> a_list += [1]
+ >>> a_list
+ [1]
+
+is equivalent to::
+
+ >>> result = a_list.__iadd__([1])
+ >>> a_list = result
+
+The object pointed to by a_list has been mutated, and the pointer to the
+mutated object is assigned back to ``a_list``. The end result of the
+assignment is a no-op, since it is a pointer to the same object that ``a_list``
+was previously pointing to, but the assignment still happens.
+
+Thus, in our tuple example what is happening is equivalent to::
+
+ >>> result = a_tuple[0].__iadd__(['item'])
+ >>> a_tuple[0] = result
+ Traceback (most recent call last):
+ ...
+ TypeError: 'tuple' object does not support item assignment
+
+The ``__iadd__`` succeeds, and thus the list is extended, but even though
+``result`` points to the same object that ``a_tuple[0]`` already points to,
+that final assignment still results in an error, because tuples are immutable.
+
+
Dictionaries
============