1

I thought that dictionaries and lists were mutable in Python. What explains this behavior?

#!/usr/bin/python3                                                             

def change_list(l):
    l = ['changed!']

def change_dict(d):                                                            
    d = {'changed!'}

mydict = {'bob': ['blah', 5], 'asdf' : 'asdf'}                                 
change_dict(mydict)                                                            
print(mydict)                                                                  

mylist = ['hello', 'foo', 'bar']
change_list(mylist)
print(mylist)

Output:

$ python3 test3.py 
{'asdf': 'asdf', 'bob': ['blah', 5]}
['hello', 'foo', 'bar']

Expected output:

$ python3 test3.py 
{'changed!'}
['changed!']
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
bvpx
  • 1,227
  • 2
  • 16
  • 33
  • 6
    Assignment is not mutation. – Kevin Jun 03 '15 at 16:19
  • How do I change the passed parameters, other than assigning them new values? – bvpx Jun 03 '15 at 16:19
  • 2
    Try e.g. `l.append('changed')` or `d['asdf'] = 'changed'`. This *changes the objects in-place*, rather than *replacing them completely*. Recommended reading: http://nedbatchelder.com/text/names.html, and see this question from the other perspective: http://stackoverflow.com/q/575196/3001761 – jonrsharpe Jun 03 '15 at 16:20
  • Wow, that's confusing. The fact that `d['asdf'] = 'changed'` works but `d = 'changed'` doesn't work is very counter-intuitive. – bvpx Jun 03 '15 at 16:21
  • 1
    @bvpx why? In one case you're changing the object, in the other you're just assigning a **completely different object** to the same name. – jonrsharpe Jun 03 '15 at 16:22
  • You could try `empty()` and then `update({ ...])` rather than re-assigning – camz Jun 03 '15 at 16:24
  • @jonrsharpe I guess I don't understand how assigning `d['asdf']` and `d` can be considered different, since they both operate on the same object (or so I thought...?). – bvpx Jun 03 '15 at 16:24
  • 1
    Remember that python names (like `d` and `l`) are not objects,, they are references to objects. So when you do an assignment you are making that name refer to a different object. – cdarke Jun 03 '15 at 16:28
  • 1
    `d['asdf'] = 'changed'` does not assign to `d`, it assigns to something referenced by `d`. It's equivalent to `d.__setitem__('asdf', 'changed')`, if that helps. – chepner Jun 03 '15 at 17:14

4 Answers4

3

Perhaps a "picture" will help:

d = ['a', 'list']

#       d -> [ 0 , 1 ]
#             /    |
#           'a' 'list'

d.append('changed')

#       d -> [ 0 , 1 , 2 ]
#             /    |    \
#           'a' 'list' 'changed'

d[0] = 'my'

#       d -> [ 0 , 1 , 2 ]
#             /    |    \
#          'my' 'list' 'changed'

d = []

# [] <- d    [ 0 , 1 , 2 ]
#             /    |    \
#          'my' 'list' 'changed'

Reassigning the name d (whether that's an argument inside a function or otherwise) does not affect the list it was previously a reference to.

This is a good introduction to how names/identifiers work in Python, and how that interacts with e.g. assignment and immutability.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
1

d, l, mydict, and mylist are variables that point to objects that exist somewhere in memory. It's important to keep in mind the distinction between the variable and the thing it points to. Variables kinda work like pronouns. If I point at a can and say "pick that up" and later I point to a box and say "pick that up," "that" refers to a different thing each time.

When you say l = ['changed'], you're telling Python to point the variable at something else, and that's all it does. It's like saying "from now on, when I say l, I mean this new list: ['changed']." But it doesn't change the pointed-to thing itself.

If you want to change an object, you need to use a technique that operates on the object itself, like assigning to a key of the object (l['0'] = 'changed').

Some languages do have support for saying "anything that referred to this object should now refer to this other object." It seems to have fallen out of style, though, because I don't know of a way to do it in any language that's currently prominent.

Erin Call
  • 1,764
  • 11
  • 15
0

You are trying to assign, not change the list/dictionary. To change the contents you can use list/dictionary methods. On the other hand, you are only trying to assign new objects.

Methods you can use for what you want to do are:

  • lists: append(), remove(), insert(), extend(),...
  • dictionaries: update(), clear(),...
geckon
  • 8,316
  • 4
  • 35
  • 59
0

You are REassinging, not changing.

And within a function your scope is different.

Douglas Denhartog
  • 2,036
  • 1
  • 16
  • 23