简体   繁体   中英

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'}

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"}

You could achieve what you want by storing the names in lists, but it makes the code more cluttered and less efficient, due to the extra layer of indirection.

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

batman = {'Batman' : ['Bruce']}
super_hero_names.update(batman)

batman['Batman'][0] = 'Bruce Wayne'
print(super_hero_names)

output

{'Superman': ['Clark Kent'], 'Spiderman': ['Peter Parker'], 'Batman': ['Bruce Wayne']}

We could also do that update using

batman['Batman'][0] += ' Wayne'

Each time you create the dictionary batman , you are actually creating a dictionary and assigning it to the variable batman

I believe what you are trying to do is this:

super_hero_names = {
    'Superman' : 'Clark Kent',
    'Spiderman' : 'Peter Parker'}
batman = {'Batman' : 'Bruce'}
super_hero_names.update(batman)
super_hero_names['Batman'] =  'Bruce Wayne'
print(super_hero_names)

The output in this case will be:

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

I hope this explains your question. When you update a dictionary it updates dictionary with the values of another dictionary, It is merging. But when you insert one dictionary into other mutability exists

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

batman = {'Batman' : 'Bruce'}

super_hero_names['new'] = batman

print(super_hero_names)

batman['Batman'] = 'Bruce Wayne'

print(super_hero_names)

batman = {'Batman' : 'Bruce Wayne'}

print(super_hero_names)

Output

{'Spiderman': 'Peter Parker', 'new': {'Batman': 'Bruce'}, 'Superman': 'Clark Kent'}
{'Spiderman': 'Peter Parker', 'new': {'Batman': 'Bruce Wayne'}, 'Superman': 'Clark Kent'}
{'Spiderman': 'Peter Parker', 'new': {'Batman': 'Bruce Wayne'}, 'Superman': 'Clark Kent'}

In this case, update() behaves like this:

for key, value in batman.items():
    super_hero_names[key] = value

Here, super_hero_names.update(batman) first checks whether super_hero_names has 'Batman' key. If exist, it overwrite (or update) the value. If not, it create 'Batman' and just assign the value to the key.


Added:

Here is a sample code. Instead of using dictionary as a data container, I used list to hold multiple superman dictionaries. As you said, dictionary is mutable, so update method modifies the contents in the list of super_hero_names .

superman = {'Superman' : 'Clark Kent'}
spiderman = {'Spiderman' : 'Peter Parker'}

super_hero_names = [
    superman,
    spiderman
]

batman = {'Batman' : 'Bruce'}
super_hero_names.append(batman)
print(super_hero_names)
# [{'Superman': 'Clark Kent'}, {'Spiderman': 'Peter Parker'}, {'Batman': 'Bruce'}]

batman.update({'Batman': 'test'})

print(super_hero_names)
# [{'Superman': 'Clark Kent'}, {'Spiderman': 'Peter Parker'}, {'Batman': 'test'}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM