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)

@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
Qoto Mastodon

QOTO: Question Others to Teach Ourselves
An inclusive, Academic Freedom, instance
All cultures welcome.
Hate speech and harassment strictly forbidden.