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?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Andrew
  • 669
  • 7
  • 16

1 Answers1

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
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 4
    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. – Claudiu Aug 20 '15 at 20:40
  • 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. – Andrew Aug 20 '15 at 20:57
  • 1
    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`? – nalzok Sep 07 '17 at 14:14
  • 2
    @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. – Martijn Pieters Sep 07 '17 at 14:28
  • 1
    @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`. – Martijn Pieters Sep 07 '17 at 15:04
  • 1
    @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. – nalzok Sep 07 '17 at 15:13
  • 1
    @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)}` – nalzok Sep 07 '17 at 15:15
  • 2
    @SunQingyao: exactly. Assigning the tuple to a key in that dictionary you created a reference to itself. – Martijn Pieters Sep 07 '17 at 15:17