4

I understand that when you do a shallow copy of a dictionary, you actually make a copy of the references. So if I do this:

x={'key':['a','b','c']}
y=x.copy()

So the reference of the list ['a','b','c'] is copied into y. Whenever I change the list ( x['key'].remove('a') for example), both dict x and y will change. This part I understand. But when I consider situation like this below:

x={'user':'admin','key':['a','b','c']}
y=x.copy()

When I do y['user']='guest', x['user'] will not change, but the list still shares the same reference. So my question is what makes the string different than the list? What is the mechanism behind this?

jujae
  • 51
  • 4

2 Answers2

14

You're doing two different things. When you do

x['key'].remove('a')

you mutate the object that x['key'] references. If another variable references the same object, you'll see the change from that point of view, too:

Pythontutor visualization Pythontutor visualization 2

However, in the second case, the situation is different:

PT vis 3

If you do

y['user']='guest'

you rebind y['user'] to a new object. This of course does not affect x['user'] or the object it references.

This has nothing to do with mutable vs. immutable objects, by the way. If you did

x['key'] = [1,2,3]

you wouldn't change y['key'] either:

PT vis 4

See it interactively on PythonTutor.com.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • For some good supplemental reading that unpacks this a little more, see [Facts and myths about Python names and values](http://nedbatchelder.com/text/names.html). – Steven Rumbalski Jul 25 '13 at 18:21
  • So if I consider the y['user'] as a pointer, it contains the address let's say Ox0001 pointed to the address of the string 'admin', but when i do y['user']='guest', python will assign a new "memory block"Ox0002 for the string 'guest' and update the address inside the y['user'] into the new address 0x0002? – jujae Jul 25 '13 at 18:27
  • @jujae: That's about what happens, yes. – Tim Pietzcker Jul 25 '13 at 18:32
5

The difference is that in once case, you are assigning a new value to the dictionary key, whereas in the other case, you are modifying the existing value. Notice the difference in your two pieces of code:

x['key'].remove('a')

There is no = sign here. You are not assigning anything in the dictionary. In fact, the dictionary hardly even "knows" what's going on. You're just reaching in and manipulating an object inside the dictionary.

y['user'] = 'guest'

Here you are actually assigning a new value to the dictionary key.

You cannot do the equivalent of remove in the second case because strings are immutable. However, the difference is not "because strings are immutable". The difference is that you are mutating the list and not the string. You can get the behavior of the second example in the first case by doing

x['key'] = ['new', 'list']

This will assign a new value to the key in x, leaving y unaffected.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384