Still one of the oldest #Python oddities I've ever seen 🐍🤔

>>> a = ([],)
>>> a[0] += ["what"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a
(['what'],)

Note that an exception was raised but the operation also worked! 😳 #pythonoddity

@treyhunner Ha! That's a good one, it really illustrates how `+=` is just syntactic sugar over the `+` and the `=`

@chris @treyhunner @s_gruppetta

It's weirder than that!

`a[0] = a[0] + ["what"]` reports the error but doesn't have the side effect.

`a[0] += ["what"]` reports the error and DOES have the side effect!

I'm shocked both that the failed assignment has a side effect and that these do not behave the same way. Can anyone explain?

@peterdrake @chris @treyhunner Trey is the master at these things, but here's my attempt.

In the first one, the expression after the `=` is executed first; this is fine as you're adding one list to another (using `list.__add__()`). This creates a new object. Then, there's an attempt to assign this new object as the first item in `a` which fails since `a` is a tuple.

In the second, the assignment and addition are attempted together using the `__iadd__()` dunder method, rather than `__add__()`. No new object is created.

Here's a different example which may shed some light:

`>>> b = [2]`
`>>> c = [3]`
`>>> id(b)`
`4407498688`
`>>> b = b + c`
`>>>`id(b)`
`4407508864`

`b` has a different id after the assignment, so it's a different object to the original `b`

now, in a new session:

`>>> b = [2]`
`>>> c = [3]`
`>>> id(b)`
`4394855808`
`>>> b += c`
`>>> id(b)`
`4394855808`

`b` is now the same object as it was before (same id)

@peterdrake @chris @treyhunner Oops, markdown error (shame there's no preview), the id for `b` in the first example should be on a new line, of course

@peterdrake @chris @treyhunner This shows that `+=` is not quite just syntactic sugar for the separate `=` and `+`. One creates a new object; the other doesn't

@s_gruppetta @peterdrake @treyhunner I think Stephen nailed it with the observation that one is an `__add__` that makes a new `list`, versus an `__iadd__` that modifies the `list`

@s_gruppetta @chris @treyhunner Fascinating!

I can imagine efficiency reasons for these being different.

It still seems wrong that __iadd__ for tuples makes a change and *then* throws an error. Is there a reason for this behavior?

@peterdrake @s_gruppetta @chris it's actually __iadd__ on a list that's being used here.

a[0] += ["huh"]

Checks if a[0] has a __iadd__ method, which of it does because it's a list. So that line is translated to:

a[0] = a[0].__iadd__(["huh"])

The list __iadd__ method succeeds.

But then assigning to a[0] (which happens after the right-hand side of that assignment) fails.

Sign in to participate in the conversation
CleverLibre Social

CleverLibre Social is an inclusive social instance for open discussion, learning, and community.
All cultures welcome.
Hate speech and harassment strictly forbidden.