This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Unexpected behaviour for += assignment to list inside tuple
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.10, Python 3.9, Python 3.8
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Adam Cmiel, remi.lapeyre
Priority: normal Keywords:

Created on 2020-06-08 14:58 by Adam Cmiel, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (4)
msg370990 - (view) Author: Adam Cmiel (Adam Cmiel) Date: 2020-06-08 14:58
Python version:

Python 3.8.3 (default, May 15 2020, 00:00:00) 
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux

Description:

When assigning to a tuple index using +=, if the element at that index is a list, the list is extended and a TypeError is raised.

a = ([],)
try:
    a[0] += [1]
except TypeError:
    assert a != ([1],)  # assertion fails
else:
    assert a == ([1],)

The expected behaviour is that only one of those things would happen (probably the list being extended with no error, given that a[0].extend([1]) works fine).
msg370991 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-06-08 15:20
Alas, this gotcha is the consequence of the way `+=` is defined in Python. Although the behaviour is surprising, there's nothing to fix because everything is working as expected:

* addition to a list will extend the list, as expected;
* trying to assign into a tuple will fail, as expected.

It's just the combination that is surprising.

There's even a FAQ for it:

https://docs.python.org/3/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works
msg370992 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2020-06-08 15:27
Multiple steps happens at once here, first the list is extended, then the result is written back to the tuple, at which point it raises TypeError because you can't write to a tuple. When TypeError is raised, the list has already be extended. The code is equivalent to:


a = ([],)
try:
    b = a[0]
    b += [1]
    a[0] = b
except TypeError:
    assert a != ([1],)  # assertion fails
else:
    assert a == ([1],)


This is explained in the documentation at https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements:


An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.

Unlike normal assignments, augmented assignments evaluate the left-hand side before evaluating the right-hand side. For example, a[i] += f(x) first looks-up a[i], then it evaluates f(x) and performs the addition, and lastly, it writes the result back to a[i].
msg371000 - (view) Author: Adam Cmiel (Adam Cmiel) Date: 2020-06-08 15:44
Got it, I didn't realize that the last step of augmented assignment is (in this case) assigning the result of __iadd__ back to the tuple.

Thanks for the explanations!
History
Date User Action Args
2022-04-11 14:59:32adminsetgithub: 85088
2022-01-18 14:26:28mark.dickinsonlinkissue46423 superseder
2020-06-08 15:44:58Adam Cmielsetmessages: + msg371000
2020-06-08 15:27:14remi.lapeyresetnosy: + remi.lapeyre, - steven.daprano
messages: + msg370992
versions: + Python 3.9, Python 3.10
2020-06-08 15:20:27steven.dapranosetstatus: open -> closed

nosy: + steven.daprano
messages: + msg370991

resolution: not a bug
stage: resolved
2020-06-08 14:58:25Adam Cmielcreate