Search code examples
pythonpython-3.xdictionaryimmutabilitymutable

If dicts in python are mutable, why does editing a dict contained in a second dict not change the second dict?


I'm new to python and am just learning about immutable and mutable objects (I've never came across this before with my limited coding experience in MATLAB and C#).

I wanted to know why, if dicts in python are mutable, does editing a dict contained in a second dict not change the second dict?

Here is an example, where a dict (batman) is added to a dict of superhero names (super_hero_names). When batman is later changed, it isn't reflected in the superhero names dict. This makes sense to me if dicts were immutable like strings, but they are mutable so why is this happening?

super_hero_names = {
    'Superman' : 'Clark Kent',
    'Spiderman' : 'Peter Parker'
}

batman = {'Batman' : 'Bruce'}

super_hero_names.update(batman)

batman['Batman'] = 'Bruce Wayne' # (edited)

print(super_hero_names)

# Output: {'Superman': 'Clark Kent', 'Spiderman': 'Peter Parker', 'Batman': 'Bruce'}

Solution

  • Mutable names

    The problem in your code is that strings are immutable: you cannot modify the string 'Bruce' into 'Bruce Wayne'. You replace it and the reference is gone. If you use a mutable object as value, you can achieve the desired result:

    class Person:
        def __init__(self, name):
            self.name = name
    
        def __repr__(self):
            return repr(self.name)
    
    
    super_hero_names = {
        'Superman': Person('Clark Kent'),
        'Spiderman': Person('Peter Parker')
    }
    
    bruce = Person('Bruce')
    batman = {'Batman': bruce}
    
    super_hero_names.update(batman)
    
    bruce.name = 'Bruce Wayne'
    
    print(super_hero_names)
    # {'Superman': 'Clark Kent', 'Spiderman': 'Peter Parker', 'Batman': 'Bruce Wayne'}
    

    Your example in Ruby

    Ruby and Python often have a very similar syntax. Ruby strings are mutable, so your code would work in Ruby with very few modifications:

    super_hero_names = {
        'Superman' => 'Clark Kent',
        'Spiderman' => 'Peter Parker'
    }
    
    batman = {'Batman' => 'Bruce'}
    
    super_hero_names.update(batman)
    
    batman['Batman'] << ' Wayne' # Mutates the string, doesn't replace it!
    
    print(super_hero_names)
    # {"Superman"=>"Clark Kent", "Spiderman"=>"Peter Parker", "Batman"=>"Bruce Wayne"}