0

How to explain this output?

>>> a = a[0] = {'a': 1}
>>> a
{'a': 1, 0: {...}}
>>> a[0]
{'a': 1, 0: {...}}
>>> a[0][0]
{'a': 1, 0: {...}}
>>> a[0][0][0]
{'a': 1, 0: {...}}

From my understanding, the assignment statement a = a[0] = {'a': 1} should be equivalent to:

a = {'a': 1}
a[0] = {'a': 1}

So the expected output should be {'a': 1, 0: {'a': 1}.

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Jruv
  • 1,438
  • 2
  • 9
  • 10
  • 4
    `a[0]` contains self reference – Olvin Roght Aug 10 '21 at 09:27
  • 1
    Interesting, but if you do `a = b = []` they are also both assigned _the same_ list, so I guess it's more like `a = {'a': 1}; a[0] = a`, or maybe rather `tmp = {'a': 1}; a = tmp; a[0] = tmp` – tobias_k Aug 10 '21 at 09:30
  • Could you explain what _exactly_ you are asking: (a) _what_ is `{...}`, or (b) why is it `{...}` and not `{'a': 1}`? – tobias_k Aug 10 '21 at 09:32
  • 1
    Does this answer your question? [How to copy a dictionary and only edit the copy](https://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy) – MisterMiyagi Aug 10 '21 at 09:43
  • 1
    TLDR: Assignment does not create copies. – MisterMiyagi Aug 10 '21 at 09:43

2 Answers2

2

It has already been explained what {...} means here, so I'll try to answer the question: Why is a[0] assigned a self-reference and not another instance of {'a': 1}?

First, note that we have the same behaviour if we do x = y = []: x and y are assigned the same list, and not two different empty lists:

>>> x = y = []
>>> x.append(1)
>>> y
[1]

Why does this happen? Let's have a look at the disassembly:

>>> import dis
>>> dis.dis("a = a[0] = {'a': 1}")
  1           0 LOAD_CONST               0 ('a')
              2 LOAD_CONST               1 (1)
              4 BUILD_MAP                1
              6 DUP_TOP
              8 STORE_NAME               0 (a)
             10 LOAD_NAME                0 (a)
             12 LOAD_CONST               2 (0)
             14 STORE_SUBSCR
             16 LOAD_CONST               3 (None)
             18 RETURN_VALUE

(You can ignore the last two lines, they are not relevant to the assignment.) The first three lines create the map {'a': 1}, then DUP_TOP duplicates the reference to that map, then that reference is stored in both a and a[0].

Thus, the assignment can roughly be interpreted as tmp = {'a': 1}; a = tmp; a[0] = tmp

tobias_k
  • 81,265
  • 12
  • 120
  • 179
1

The ... ellipsis here indicates that your dictionary a holds a recursive reference to itself at key 0. The reason why this is represented as an ellipsis in the REPL is because the alternative would be for the python shell to continue printing your dictionary forever, which is generally undesirable.

This output makes sense, since you have specified that a = a[0], the only logical interpretation of which is that your dictionary a must hold a reference to itself at key 0. The reason why the nested __getitem__ calls return the same output is because a[0] is a; they are the same object. As a result, the following expressions will all evaluate as True:

  • a[0] is a
  • a is a[0] is a[0][0]
  • a is a[0] is a[0][0] is ... (etc)
  • id(a) == id(a[0])
  • id(a) == id(a[0]) == id(a[0][0])
  • id(a) == id(a[0]) == id(a[0][0]) == ... (etc)
Alex Waygood
  • 6,304
  • 3
  • 24
  • 46