62

When I try this code:

dict_a = dict_b = dict_c = {}
dict_c['hello'] = 'goodbye'

print(dict_a)
print(dict_b)
print(dict_c)

I expected that it would just initialise the dict_a, dict_b and dict_c dictionaries, and then assign a key in dict_c, resulting in

{}
{}
{'hello': 'goodbye'}

But it seems to have a copy-through effect instead:

{'hello': 'goodbye'}
{'hello': 'goodbye'}
{'hello': 'goodbye'}

Why?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Marcus Whybrow
  • 19,578
  • 9
  • 70
  • 90
  • No tutorial, I was just applying concepts I thought worked in languages such as Java. – Marcus Whybrow Mar 15 '10 at 15:55
  • See also: https://stackoverflow.com/questions/17246693/what-is-the-difference-between-shallow-copy-deepcopy-and-normal-assignment-oper – Karl Knechtel Apr 03 '22 at 11:09
  • 1
    "But it seams to have a copy through effect:" The behaviour here is **exactly because it doesn't** copy. Copying would mean that each variable got its own separate `{}` value. Instead, they get **the same** empty dictionary, and are just different names for it. – Karl Knechtel Aug 12 '22 at 03:33
  • 1
    It didn't exist at the time this question was asked, but those finding this question now should read https://nedbatchelder.com/text/names.html. – chepner Sep 02 '22 at 20:34

5 Answers5

58

This is because in Python, variables (names) are just references to individual objects. When you assign dict_a = dict_b, you are really copying a memory address (or pointer, if you will) from dict_b to dict_a. There is still one instance of that dictionary.

To get the desired behavior, use either the dict.copy method, or use copy.deepcopy if your dict may have nested dicts or other nested objects.

>>> a = {1:2}
>>> b = a.copy()
>>> b
{1: 2}
>>> b[3] = 4
>>> a
{1: 2}
>>> b
{1: 2, 3: 4}
>>> 
Pyrocater
  • 1,982
  • 1
  • 13
  • 13
danben
  • 80,905
  • 18
  • 123
  • 145
  • 1
    So dictionaries are objects, would this still be the case with say an integer variable or would the type of a variable have no impact on it being an object? – Marcus Whybrow Mar 13 '10 at 15:51
  • 1
    Integers are immutable so I'm not sure how you would intend to reproduce this behavior. In any case, literal values for some types are interned (ie there is only one instance of each). You can verify that two integer variables containing the same value actually point to the same instance of the integer via the `id()` function. – danben Mar 13 '10 at 15:53
  • 2
    In any case, everything in Python is an object. To see the functions that belong to the integer object 1, you can execute `dir(1)`. – danben Mar 13 '10 at 15:54
  • 18
    Everything is an object, and variables are names that we assign to objects (not the other way around!) So if you do `a = b = c = 1; a +=1`, then everything gets assigned to the object `1`, and then `a` gets assigned to the object `2`. With mutable objects like lists, dicts and sets, you can change the object itself, which will be seen by all names assigned to that object, but with ints, strings, tuples, and other immutable objects, all you can do is assign the name to a different object. – jcdyer Mar 13 '10 at 16:01
  • Ah I see, that makes perfect sense. – Marcus Whybrow Mar 13 '10 at 16:10
  • 2
    *"objects are just references"* This is not true. Objects are objects. Names reference them. – Mike Graham Mar 13 '10 at 17:35
  • Not as worded no, but I know what @danben meant, thanks for clearing up any confusion though. – Marcus Whybrow Mar 13 '10 at 18:01
10

Even though

>>> dict_a, dict_b, dict_c = {}, {}, {}

is the right way to go in most cases, when it get more than 3 it looks weird

Imagine

>>> a, b, c, d, e, f = {}, {}, {}, {}, {}, {}

In cases where I wanna initialize more than 3 things, I use

>>> a, b, c, d, e, f, = [dict() for x in range(6)]
chepner
  • 497,756
  • 71
  • 530
  • 681
Jeffrey Jose
  • 1,992
  • 2
  • 16
  • 21
  • 4
    "Caution: Do not use [{} for x in range(6)]" Why not? the element expression gets executed once for each iteration, and each time a literal `{}` gets executed, it returns a new dict. In fact, this should be preferred over `dict()` since it avoids a function call. – SingleNegationElimination Jul 22 '11 at 20:43
  • @TokenMacGuy, yes and no. Now that I re-read what I wrote I see your point. `{}` gets initialized everytime time. That said `{}` is just a syntax sugar for `dict()`. You're not saving anything by using former. – Jeffrey Jose Jul 24 '11 at 10:09
  • 6
    Jeffrey Jose: Doubting Thomas aye? `import dis; print dis.dis(lambda: dict()); print dis.dis(lambda:{})` – SingleNegationElimination Jul 25 '11 at 14:21
  • 1
    @TokenMacGuy. Doh! I should have `dis`'d before making any claims. I stand corrected. – Jeffrey Jose Jul 25 '11 at 15:52
  • Having a large number of similarly named variables is typically *not* the way to go in the first place. `dicts = {x: {} for x in ["a", "b", "c"]}`. – chepner Sep 02 '22 at 20:36
1

Your first assignment assigns the same dictionary object to the variables dict_a, dict_b, and dict_c. It is equivalent to dict_c = {}; dict_b = dict_c; dict_a = dict_c.

msalib
  • 440
  • 3
  • 6
1

As danben previously said, you're just copying the same dict into 3 variables, so that each one refers to the same object.

To get the behaviour you want, you should instantiate a different dict in each variable:

>>> dict_a, dict_b, dict_c = {}, {}, {}
>>> dict_c['hello'] = 'goodbye'
>>> print dict_a
{}
>>> print dict_b
{}
>>> print dict_c
{'hello': 'goodbye'}
>>>
feetwet
  • 3,248
  • 7
  • 46
  • 84
mdeous
  • 17,513
  • 7
  • 56
  • 60
  • Thanks, this was what I was intending to do when I stumbled upon this subject. Although its not an answer to the question, thanks very much. – Marcus Whybrow Mar 13 '10 at 18:04
0

I agree with what is said above. The key here is that, in Python, assignments represent references to the object. I was trying to grasp the concept myself and I think is it important to understand in which case a new object is created and when is the existing one changed.

In the example above, the line:

dict_c['hello'] = 'goodbye'

doesn't create a new object. It only changes the object which is referenced by dict_a, dict_b, and dict_c.

If, instead, you wrote:

dict_c = {'hello': 'goodbye'}

it would create a new object which would be referenced by dict_c. Dict_a and dict_b would still be pointing to the empty object.

In that case, if you run:

print dict_a
print dict_b
print dict_c

you would get:

{}
{}
{'hello': 'goodbye'}