Python Assignment Operator Precedence - (a, b) = a[b] = {}, 5
Asked Answered
P

1

35

I saw this Python snippet on Twitter and was quite confused by the output:

>>> a, b = a[b] = {}, 5
>>> a
{5: ({...}, 5)}

What is going on here?

Petitioner answered 20/8, 2015 at 20:36 Comment(0)
H
29

From the Assignment statements documentation:

An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.

You have two assignment target lists; a, b, and a[b], the value {}, 5 is assigned to those two targets from left to right.

First the {}, 5 tuple is unpacked to a, b. You now have a = {} and b = 5. Note that {} is mutable.

Next you assign the same dictionary and integer to a[b], where a evaluates to the dictionary, and b evaluates to 5, so you are setting the key 5 in the dictionary to the tuple ({}, 5) creating a circular reference. The {...} thus refers to the same object that a is already referencing.

Because assignment takes place from left to right, you can break this down to:

a, b = {}, 5
a[b] = a, b

so a[b][0] is the same object as a:

>>> a, b = {}, 5
>>> a[b] = a, b
>>> a
{5: ({...}, 5)}
>>> a[b][0] is a
True
Henrieta answered 20/8, 2015 at 20:39 Comment(8)
Hmm that's surprisingly unintuitive for Python! I would expect it to cascade from right to left and thus throw a NameError, with a[b] = a, b = {}, 5 working.Strawberry
Excellent answer. I figured out what was going on before I posted and was planning on posting a response, but you answered it very well. Also, +1 for the a[b][0] is a line.Petitioner
Nice answer! But the docs say assigns the single resulting object to each of the target lists, from left to right, so shouldn't it be equivalent to a, b = {}, 5; a[b] = {}, 5?Priscella
@Claudio: that order only applies in languages where assignment is an expression and thus have a result to return. That forces the right-to-left order. In Python assignment is a statement and follows the natural reading order instead.Henrieta
@SunQingyao: did you expect {}, 5 to be evaluated twice when assigning? Why should it be? {} is still the same object. It's the equivalent of d = {}; a, b = d, 5; a[b] = d, 5.Henrieta
@MartijnPieters I think it should be something like tmp0, tmp1 = {}, 5; a, b = tmp0, tmp1; a[b] = tmp0, tmp1, so that there won't be a circular reference.Priscella
@MartijnPieters Ahh I understand now! a is just a reference to {}, which is mutable! So even in tmp0, tmp1 = {}, 5; a, b = tmp0, tmp1; a[b] = tmp0, tmp1, the value (or "referenced object") of a is still {5: ({...}, 5)}Priscella
@SunQingyao: exactly. Assigning the tuple to a key in that dictionary you created a reference to itself.Henrieta

© 2022 - 2024 — McMap. All rights reserved.