Dictionaries 2 - operators

Download exercise zip

Browse online files

There are several operators to manipulate dictionaries:

Operator

Return

Description

len(dict)

int

Retorn the number of keys

dict[chiave]

obj

Return the value associated to the key

dict[chiave] = valore

Adds or modify the value associated to the key

del dict[chiave]

Removes the key/value couple

obj in dict

bool

Return True if the key obj is present in dict

==,!=

bool

Checks whether two dictionaries are equal or different

What to do

  1. Unzip exercises zip in a folder, you should obtain something like this:

sets
    dictionaries1.ipynb
    dictionaries1-sol.ipynb
    dictionaries2.ipynb
    dictionaries2-sol.ipynb
    dictionaries3.ipynb
    dictionaries3-sol.ipynb
    dictionaries4.ipynb
    dictionaries4-sol.ipynb
    dictionaries5.ipynb
    dictionaries5-sol.ipynb
    jupman.py

WARNING: to correctly visualize the notebook, it MUST be in an unzipped folder !

  1. open Jupyter Notebook from that folder. Two things should open, first a console and then a browser. The browser should show a file list: navigate the list and open the notebook dictionaries2.ipynb

  2. Go on reading the exercises file, sometimes you will find paragraphs marked Exercises graded from ✪ to ✪✪✪✪ which will ask to write Python commands in the following cells.

Shortcut keys:

  • to execute Python code inside a Jupyter cell, press Control + Enter

  • to execute Python code inside a Jupyter cell AND select next cell, press Shift + Enter

  • to execute Python code inside a Jupyter cell AND a create a new cell aftwerwards, press Alt + Enter

  • If the notebooks look stuck, try to select Kernel -> Restart

len

We can obtain the number of key/value associations in a dictionary by using the function len:

[2]:
len({'a':5,
     'b':9,
     'c':7
})
[2]:
3
[3]:
len({3:8,
     1:3
})
[3]:
2
[4]:
len({})
[4]:
0

QUESTION: Look at the following expressions, and for each try guessing the result (or if it gives an error):

  1. len(dict())
    
  2. len({'a':{}})
    
  3. len({(1,2):{3},(4,5):{6},(7,8):{9}})
    
  4. len({1:2,1:2,2:4,2:4,3:6,3:6})
    
  5. len({1:2,',':3,',':4,})
    
  6. len(len({3:4,5:6}))
    

Reading a value

At the end of dictionaries definition, it is reported:

Given a key, we can find the corresponding value very fast

How can we specify the key to search? It’s sufficient to use square brackets [ ], a bit like we already did for lists:

[5]:

furniture = {
      'chair'    : 'a piece of furniture to sit on',
      'cupboard' : 'a cabinet for storage',
      'lamp'     : 'a device to provide illumination'
    }

[6]:
furniture['chair']
[6]:
'a piece of furniture to sit on'
[7]:
furniture['lamp']
[7]:
'a device to provide illumination'

WARNING: What we put in square parenthesis must be a key present in the dictionary

If we put keys which are not present, we will get an error:

>>> furniture['armchair']

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-19-ee891f51417b> in <module>
----> 1 furniture['armchair']

KeyError: 'armchair'

Fast disorder

Whenever we give a key to Python, how fast is it in getting the corresponding value? Very fast, so much so the speed does not depend on the dictionary dimension. Whether it is small or huge, given a key it will always find the associated value in about the same time.

When we hold a dictionary in real life, we typically have an item to search for and we turn pages until we get what we’re looking for: the fact items are sorted allows us to rapidly find the item.

We might expect the same also in Python, but if we look at the definition we find a notable difference:

Dictionaries are mutable containers which allow us to rapidly associate elements called keys to some values

Keys are immutable, don’t have order and there cannot be duplicates Values can be duplicated

If keys are not ordered, how can Python get the values so fast? The speed stems from the way Python memorizes keys, which is based on hashes, similarly for what happens with sets. The downside is we can only immutable objects as keys.

QUESTION: If we wanted to print the value 'a device to provide illumination' we see at the bottom of the dictionary, without knowing it corresponds to lamp, would it make sense to write something like this?

furniture = {'chair':'a piece of furniture to sit on',
             'cupboard':'a cabinet for storage',
             'lamp': 'a device to provide illumination'
}

print( furniture[2] )
Show answer

QUESTION: Look at the following expressions, and for each try guessing which result it produces (or if it gives an error):

kabbalah = {
    1 : 'Progress',
    3 : 'Love',
    5 : 'Creation'
}
  • kabbalah[0]
    
  • kabbalah[1]
    
  • kabbalah[2]
    
  • kabbalah[3]
    
  • kabbalah[4]
    
  • kabbalah[5]
    
  • kabbalah[-1]
    
Show answer

QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):

  1. {'a':4,'b':5}('a')
    
  2. {1:2,2:3,3:4}[2]
    
  3. {'a':1,'b':2}['c']
    
  4. {'a':1,'b':2}[a]
    
  5. {'a':1,'b':2}[1]
    
  6. {'a':1,'b':2,'c':3}['c']
    
  7. {'a':1,'b':2,'c':3}[len(['a','b','c'])]
    
  8. {(3,4):(1,2)}[(1,2)]
    
  9. {(1,2):(3,4)}[(1,2)]
    
  10. {[1,2]:[3,4]}[[1,2]]
    
  11. {'a','b','c'}['a']
    
  12. {'a:b','c:d'}['c']
    
  13. {'a':4,'b':5}{'a'}
    
  14. d1 = {'a':'b'}
    d2 = {'b':'c'}
    print(d1[d2['c']])
    
  15. d1 = {'a':'b'}
    d2 = {'b':'c'}
    print(d2[d1['a']])
    
  16. {}[]
    
  17. {[]:3}[[]]
    
  18. {1:7}['1']
    
  19. {'':7}"[]"
    
  20. {'':7}[""]
    
  21. {"":7}['']
    
  22. {'"':()}['']
    
  23. {():7}[()]
    
  24. {(()):7}[()]
    
  25. {(()):7}[((),)]
    

Exercise - z7

✪ Given a dictionary d1 with keys 'b' and 'c' and integer values, create a dictionary d2 containing the key 'z' and associate to it the sum of values of keys from d1

  • your code must work for any d1 with keys 'b' and 'c'

Example - given:

d1 = {'a':6, 'b':2,'c':5}

After your code, it must result:

>>> print(d2)
{'z': 7}
Show solution
[8]:
d1 = {'a':6, 'b':2,'c':5}

# write here


{'z': 7}

Writing in the dictionary

Can we write in a dictionary?

Dictionaries are mutable containers which allow us to rapidly associate elements called keys to some values

The definition talks about mutability, so we are allowed to modify dictionaries after creation.

Dictionaries are collections of key/value couples, and among the possible modifications we find:

  1. adding a key/value couple

  2. associate an existing key to a different value

  3. remove a key/value couple

Writing - adding key/value

Suppose we created our dictionary furniture:

[9]:

furniture = {
    'chair'    : 'a piece of furniture to sit on',
    'cupboard' : 'a cabinet for storage',
    'lamp'     : 'a device to provide illumination'
}

and afterwards we want to add a definition for 'armchair'. We can reuse the variable furniture followed by square brackets with inside the key we want to add ['armchair'] and after the brackets we will put an equality sign =

[10]:
furniture['armchair'] = 'a chair with armrests'

Note Jupyter didn’t show results, because the previous operation is an assignment command (only expressions generate results).

But something did actually happen in memory, we can check it by furniture:

[11]:
furniture
[11]:
{'armchair': 'a chair with armrests',
 'chair': 'a piece of furniture to sit on',
 'cupboard': 'a cabinet for storage',
 'lamp': 'a device to provide illumination'}

Note the dictionary associated to the variable furniture was MODIFIED with the addition of 'armchair'.

When we add a key/value couple, we can use heterogenous types:

[12]:
trashcan = {
    'bla' : 3,
     4    : 'boh',
    (7,9) : ['gar','bage']
}
[13]:
trashcan[5.0] = 'a float'
[14]:
trashcan
[14]:
{(7, 9): ['gar', 'bage'], 4: 'boh', 5.0: 'a float', 'bla': 3}

We are subject to the same constraints on keys we have during the creation, so we can only use immutable keys. If we try inserting a mutable type, for example a list, we will get an error:

>>> trashcan[ ['some', 'list']  ] = 8

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-51-195ac9c21bcd> in <module>
----> 1 trashcan[ ['some', 'list']  ] = 8

TypeError: unhashable type: 'list'

QUESTION: Look at the following expressions, and for each try guessing the result (or if gives an error):

  1. d = {1:'a'}
    d[2] = 'a'
    print(d)
    
  2. d = {}
    print(len(d))
    d['a'] = 'b'
    print(len(d))
    
  3. d1 = {'a':3, 'b':4}
    diz2 = diz1
    diz1['a'] = 5
    print(diz1)
    print(diz2)
    
  4. diz1 = {'a':3, 'b':4}
    diz2 = dict(diz1)
    diz1['a'] = 5
    print(diz1)
    print(diz2)
    
  5. la = ['a','c']
    diz = {'a':3,
           'b':4,
           'c':5}
    diz['d'] = diz[la[0]] + diz[la[1]]
    print(diz)
    
  6. diz = {}
    diz[()]: ''
    diz[('a',)]: 'A'
    diz[('a','b')]: 'AB'
    print(diz)
    
  7. la = [5,8,6,9]
    diz = {}
    diz[la[0]]=la[2]
    diz[la[2]]=la[0]
    print(diz)
    
  8. diz = {}
    diz[(4,5,6)[2]] = 'c'
    diz[(4,5,6)[1]] = 'b'
    diz[(4,5,6)[0]] = 'a'
    print(diz)
    
  9. diz1 = {
        'a' : 'x',
        'b' : 'x',
        'c' : 'y',
        'd' : 'y',
    }
    
    diz2 = {}
    diz2[diz1['a']] = 'a'
    diz2[diz1['b']] = 'b'
    diz2[diz1['c']] = 'c'
    diz2[diz1['d']] = 'd'
    print(diz2)
    

Writing - reassociate a key

Let’s suppose to change the definition of a lamp:

[15]:
furniture = {'chair':'a piece of furniture to sit on',
             'cupboard':'a cabinet for storage',
             'lamp': 'a device to provide illumination'
}
[16]:
furniture['lamp'] = 'a device to provide visible light from electric current'
[17]:
furniture
[17]:
{'chair': 'a piece of furniture to sit on',
 'cupboard': 'a cabinet for storage',
 'lamp': 'a device to provide visible light from electric current'}

Exercise - workshop

✪ MODIFY the dictionary workshop:

  1. set the 'bolts' key value equal to the value of the 'pincers' key

  2. increment the value of wheels key of 1

  • your code must work with any number associated to the keys

  • DO NOT create new dictionaries, so no lines beginning with workshop = {

Example - given:

workshop = {'wheels':3,
            'bolts':2,
            'pincers':5}

after your code, you should obtain:

>>> print(workshop)
{'bolts': 5, 'wheels': 4, 'pincers': 5}
Show solution
[18]:
workshop = {'wheels' : 3,
            'bolts'  : 2,
            'pincers': 5}

# write here


QUESTION: Look at the following code fragments expressions, and for each try guessing the result it produces (or if it gives an error):

  1. diz = {'a':'b'}
    diz['a'] = 'a'
    print(diz)
    
  2. diz = {'1':'2'}
    diz[1] = diz[1] + 5   # nasty
    print(diz)
    
  3. diz = {1:2}
    diz[1] = diz[1] + 5
    print(diz)
    
  4. d1 = {1:2}
    d2 = {2:3}
    d1[1] = d2[d1[1]]
    print(d1)
    

Writing - deleting

To remove a key/value couple the special command del is provided. Let’s take a dictionary:

[19]:
kitchen = {
    'pots'  : 3,
    'pans'  : 7,
    'forks' : 20
}

If we want to eliminate the couple pans : 7, we will write del followed by the name of the dictionary and the key to eliminate among square brackets:

[20]:
del kitchen['pans']
[21]:
kitchen
[21]:
{'forks': 20, 'pots': 3}

Trying to delete a non-existemt key will produce an error:

>>> del cucina['crankshaft']

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-34-c0d541348698> in <module>
----> 1 del cucina['crankshaft']

KeyError: 'crankshaft'

QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):

  1. diz = {'a':'b'}
    del diz['b']
    print(diz)
    
  2. diz = {'a':'b', 'c':'d'}
    del diz['a']
    print(diz)
    
  3. diz = {'a':'b', 'c':'d'}
    del diz['a']
    del diz['a']
    print(diz)
    
  4. diz = {'a':'b'}
    new_diz = del diz['a']
    print(diz)
    print(new_diz)
    
  5. diz1 = {'a':'b', 'c':'d'}
    diz2 = diz1
    del diz1['a']
    print(diz1)
    print(diz2)
    
  6. diz1 = {'a':'b', 'c':'d'}
    diz2 = dict(diz1)
    del diz1['a']
    print(diz1)
    print(diz2)
    
  7. diz = {'a':'b'}
    del diz['c']
    print(diz)
    
  8. diz = {'a':'b'}
    diz.del('a')
    print(diz)
    
  9. diz = {'a':'b'}
    diz['a'] = None
    print(diz)
    

Exercise - desktop

Given a dictionary desktop:

desktop = {
    'paper'  :5,
    'pencils':2,
    'pens'   :3
}

write some code which MODIFIES it so that after executing your code, the dictionary appears like this:

>>> print(desktop)
{'pencil sharpeners': 1, 'paper': 5, 'pencils': 2, 'papers': 4}
  • DO NOT write lines which begin with desktop =

Show solution
[22]:
desktop = {
    'paper'  :5,
    'pencils':2,
    'pens'   :3
}

# write here


Exercise - garden

You have a dictionary garden which associates the names of present objects and their quantity. You are given:

  • a list to_remove containing the names of exactly two objects to eliminate

  • a dictionary to_add containing exactly two names of flowers associated to their quantity to add

MODIFY the dictionary garden according to the quantities given in to_remove (deleting the keys) and to_add (increasing the corresponding values)

  • assume that garden always contains the objects given in to_remove and to_add

  • assume that to_add always and only contains tulips and roses

Example - given:

to_remove = ['weeds', 'litter']
to_add = { 'tulips':4,
           'roses' : 2
}

garden = { 'sunflowers':3,
           'tulips':7,
           'weeds' : 10,
           'roses' : 5,
           'litter' : 6,
}

after your code, it must result:

>>> print(garden)
{'roses': 7, 'tulips': 11, 'sunflowers': 3}
Show solution
[23]:
to_remove = ['weeds', 'litter']
to_add = { 'tulips':4,
           'roses' : 2
}

garden = { 'sunflowers':3,
           'tulips':7,
           'weeds' : 10,
           'roses' : 5,
           'litter' : 6,
}

# write here


Exercise - translations

Given two dictionaries en_it and it_es of English-Italian and Italian-Spanish translations, write some code which MODIFIES a third dictionary en_es by placing translations from English to Spanish

  • assume that en_it always and only contains translations of hello and road

  • assume that it_es always and only contains translations of ciao and strada

  • in the solution, ONLY use the constants 'hello' and 'road', you will take the others you need from the dictionaries

  • DO NOT create a new dictionary - so no lines beginning with en_es = {

Example - given:

en_it = {
    'hello' : 'ciao',
    'road' : 'strada'
}

it_es = {
    'ciao' : 'hola',
    'strada' : 'carretera'
}
en_es = {}

after your code, it must print:

>>> print(en_es)
{'hello': 'hola', 'road': 'carretera'}
Show solution
[24]:
en_it = {
    'hello' : 'ciao',
    'road' : 'strada'
}

it_es = {
    'ciao' : 'hola',
    'strada' : 'carretera'
}

en_es = {}

# write here


Membership with in

We can check whether a key is present in a dictionary by using the operator in:

[25]:
'a' in {'a':5,'b':7}
[25]:
True
[26]:
'b' in {'a':5,'b':7}
[26]:
True
[27]:
'z' in {'a':5,'b':7}
[27]:
False

WARNING: in searches among the keys, not in values!

[28]:
5 in {'a':5,'b':7}
[28]:
False

As always when dealing with keys, we cannot search for a mutable object, like for example lists:

>>> [3,5] in {'a':'c','b':'d'}

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-41-3e3e336117aa> in <module>
----> 1 [3,5] in {'a':'c','b':'d'}

TypeError: unhashable type: 'list'

not in

It is possible to check for non belonging with the not in operator:

[29]:
'z' not in {'a':5,'b':7}
[29]:
True
[30]:
'a' not in {'a':5,'b':7}
[30]:
False

Equivalently, we can use this other form:

[31]:
not 'z' in {'a':5,'b':7}
[31]:
True
[32]:
not 'a' in {'a':5,'b':7}
[32]:
False

QUESTION: Look at the following expressions, and for each try guessing the result (or if it gives an error):

  1. ('a') in {'a':5}
    
  2. ('a','b') in {('a','b'):5}
    
  3. ('a','b',) in {('a','b'):5}
    
  4. ['a','b'] in {('a','b'):5}
    
  5. {3: 'q' in {'q':5}}
    
  6. {'q' not in {'q':0} : 'q' in {'q':0}}
    
  7. {'a' in 'b'}
    
  8. {'a' not in {'b':'a'}}
    
  9. len({'a':6,'b':4}) in {1:2}
    
  10. 'ab' in {('a','b'): 'ab'}
    
  11. None in {}
    
  12. None in {'None':3}
    
  13. None in {None:3}
    
  14. not None in {0:None}
    

Dictionaries of sequences

So far we almost always associated a single value to keys. What if wanted to associate more? For example, suppose we are in a library and we want to associate users with the books they borrowed. We could represent everything as a dictionary where a list of borrowed books is associated to each customer:

[33]:
loans = {'Marco':  ['Les Misérables', 'Ulysses'],
         'Gloria': ['War and Peace'],
         'Rita':   ['The Shining','Dracula','1984']}

Let’s see how it gets represented in Python Tutor:

[34]:
# WARNING: FOR PYTHON TUTOR TO WORK, REMEMBER TO EXECUTE THIS CELL with Shift+Enter
#          (it's sufficient to execute it only once)

import jupman
[35]:
loans = {'Marco':  ['Les Misérables', 'Ulysses'],
         'Gloria': ['War and Peace'],
         'Rita':   ['The Shining','Dracula','1984']}
jupman.pytut()
[35]:

If we try writing the expression:

[36]:
loans['Rita']
[36]:
['The Shining', 'Dracula', '1984']

Python shows the corresponding list: for all intents and purposes Python considers loans['Rita'] as if it were a list, and we can use it as such. For example, if we wanted to access the 1-indexed book of the list, we would write [1] after the expression:

[37]:
loans['Rita'][1]
[37]:
'Dracula'

Equivalently, we might also save a pointer to the list by assigning the expression to a variable:

[38]:
ritas_list = loans['Rita']
[39]:
ritas_list
[39]:
['The Shining', 'Dracula', '1984']
[40]:
ritas_list[1]
[40]:
'Dracula'

Let’s see everything in Python Tutor:

[41]:
loans = {'Marco':  ['Les Misérables', 'Ulysses'],
         'Gloria': ['War and Peace'],
         'Rita':   ['The Shining','Dracula','1984']}
ritas_list = loans['Rita']
print(rita_list[1])

jupman.pytut()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-41-678825b512c3> in <module>()
      3          'Rita':   ['The Shining','Dracula','1984']}
      4 ritas_list = loans['Rita']
----> 5 print(rita_list[1])
      6
      7 jupman.pytut()

NameError: name 'rita_list' is not defined

If you execute the code in Python Tutor, you will notice that as soon as we assign ritas_list, the corresponding list appears to ‘detach’ from the dictionary. This is only a graphical effect caused by Python Tutor, but from the point of view of the dictionary nothing changed. The intention is to show the list now is reachable both from the dictionary and from the new variable ritas_list.

Exercise - loans

Write some code to extract and print:

  1. The first book borrowed by Gloria ('War and Peace') and the last one borrowed by Rita ('1984')

  2. The number of books borrowed by Rita

  3. True if everybody among Marco, Gloria and Rita borrowed at least a book, False otherwise

Show solution
[42]:
loans = {'Marco':  ['Les Misérables', 'Ulysses'],
         'Gloria': ['War and Peace'],
         'Rita':   ['The Shining','Dracula','1984']}

# write here


1. The first book borrowed by Gloria is War and Peace
   The last book borrowed by Rita is 1984
2. Rita borrowed 3 book(s)
3. Have everybody borrowed at least a book? True

Equality

We can verify whether two dictionaries are equal with == operator, which given two dictionaries return True if they contain kequal ey/value couples or False otherwise:

[43]:
{'a':3, 'b':4} == {'a':3, 'b':4}
[43]:
True
[44]:
{'a':3, 'b':4} == {'c':3, 'b':4}
[44]:
False
[45]:
{'a':3, 'b':4} == {'a':3, 'b':999}
[45]:
False

We can verify equality of dictionaries with a different number of elements:

[46]:
{'a':3, 'b':4} == {'a':3}
[46]:
False
[47]:
{'a':3, 'b':4} == {'a':3,'b':3,'c':5}
[47]:
False

… and with heterogenous elements:

[48]:
{'a':3, 'b':4} == {2:('q','p'), 'b':[99,77]}
[48]:
False

Equality and order

From the definition:

  • Keys are immutable, don’t have order and there cannot be duplicates

Since order has no importance, dictionaries created by inserting the same key/value couples in a differenct order will be considered equal.

For example, let’s try direct creation:

[49]:
{'a':5, 'b':7} == {'b':7, 'a':5}
[49]:
True

What about incremental update?

[50]:
diz1 = {}
diz1['a'] = 5
diz1['b'] = 7

diz2 = {}
diz2['b'] = 7
diz2['a'] = 5

print(diz1 == diz2)
True

QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):

  1. {1:2} == {2:1}
    
  2. {1:2,3:4} == {3:4,1:2}
    
  3. {'a'.upper():3} == {'a':3}
    
  4. {'A'.lower():3} == {'a':3}
    
  5. {'a': {1:2} == {3:4}}
    
  6. diz1 = {}
    diz1[2] = 5
    diz1[3] = 7
    
    diz2 = {}
    diz2[3] = 7
    diz2[2] = 5
    print(diz1 == diz2)
    
  7. diz1 = {'a':3,'b':8}
    diz2 = diz1
    diz1['a'] = 7
    print(diz1 == diz2)
    
  8. diz1 = {}
    diz1['a']=3
    diz2 = diz1
    diz2['a']=4
    print(diz1 == diz2)
    
  9. diz1 = {}
    diz1['a']=3
    diz2 = diz1
    diz2['a']=4
    print(diz1 == diz2)
    
  10. diz1 = {'a':3, 'b':4, 'c':5}
    diz2 = {'a':3,'c':5}
    del diz1['a']
    print(diz1 == diz2)
    
  11. diz1 = {}
    diz2 = {'a':3}
    diz1['a'] = 3
    diz1['b'] = 5
    diz2['b'] = 5
    print(diz1 == diz2)
    

Equality and copies

When duplicating containers which hold mutable objects, if we do not pay attention we might get surprises. Let’s go back on the topic of shallow and deep copies of dictionaries, this time trying to verify the effective equality in Python.

WARNING: Have you read Dictionaries 1 - Copying a dictionary ?

If not, do it now!

QUESTION: Let’s see a simple example, with a ‘manual’ copy. If you execute the following code in Python Tutor, what will it print? How many memory regions will you see?

d1 = {'a':3,
      'b':8}
d2 = {'a':d1['a'],
      'b':d1['b'] }
d1['a'] = 6

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)

NOTE: all values (3 and 8) are immutable.

Show answer
[51]:
d1 = {'a':3,
      'b':8}
d2 = {'a':d1['a'],
      'b':d1['b'] }
d1['a'] = 6

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)

jupman.pytut()
equal? False
d1= {'a': 6, 'b': 8}
d2= {'a': 3, 'b': 8}
[51]:

QUESTION: If you execute the following code in Python Tutor, what will it print?

  1. Which type of copy did we do? Shallow? Deep? (or both …?)

  2. How many memory regions will you see?

d1 = {'a':3,
      'b':8}
d2 = dict(d1)
d1['a'] = 7

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)
Show answer
[52]:
d1 = {'a':3,
      'b':8}
d2 = dict(d1)
d1['a'] = 7

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)

jupman.pytut()
equal? False
d1= {'a': 7, 'b': 8}
d2= {'a': 3, 'b': 8}
[52]:

QUESTION: If you execute the following code in Python Tutor, what will it print?

  1. Which type of copy did we do? Shallow? Deep? (or both …?)

  2. How many memory regions will you see?

NOTE: the values are lists, thus they are mutable

d1 = {'a':[1,2],
      'b':[4,5,6]}
d2 = dict(d1)
d1['a'].append(3)

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)
Show answer
[53]:
d1 = {'a':[1,2],
      'b':[4,5,6]}
d2 = dict(d1)
d1['a'].append(3)

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)

jupman.pytut()
equal? True
d1= {'a': [1, 2, 3], 'b': [4, 5, 6]}
d2= {'a': [1, 2, 3], 'b': [4, 5, 6]}
[53]:

QUESTION: If you execute the following code in Python Tutor, what will it print?

  1. Which type of copy did we do? Shallow? Deep? (or both …?)

  2. How many memory regions will you see?

NOTE: the values are lists, so they are mutable

import copy
d1 = {'a':[1,2],
      'b':[4,5,6]}
d2 = copy.deepcopy(d1)
d1['a'].append(3)

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)
Show answer
[54]:
import copy
d1 = {'a':[1,2],
      'b':[4,5,6]}
d2 = copy.deepcopy(d1)
d1['a'].append(3)

print('equal?', d1 == d2)
print('d1=', d1)
print('d2=', d2)

jupman.pytut()
equal? False
d1= {'a': [1, 2, 3], 'b': [4, 5, 6]}
d2= {'a': [1, 2], 'b': [4, 5, 6]}
[54]:

QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):

  1. diz1 = {'a':[4,5],
            'b':[6,7]}
    diz2 = dict(diz1)
    diz2['a'] = diz1['b']
    diz2['b'][0] = 9
    print(diz1 == diz2)
    print(diz1)
    print(diz2)
    
  2. da = {'a':['x','y','z']}
    db = dict(da)
    db['a'] = ['w','t']
    dc = dict(db)
    print(da)
    print(db)
    print(dc)
    
  3. import copy
    
    la = ['x','y','z']
    diz1 = {'a':la,
            'b':la }
    diz2 = copy.deepcopy(diz1)
    diz2['a'][0] = 'w'
    print('uguali?', diz1 == diz2)
    print('diz1=', diz1)
    print('diz2=', diz2)
    

Exercise - ZOOM DOOM

Write some code which given a string s (i.e. 'ZOOM'), creates a dictionary zd and assigns to keys 'a', 'b' and 'c' the same identical list containing the string characters as elements (i.e. ['Z','O','O','M']).

  • in Python Tutor you should see 3 arrows which go from keys to the same identical memory region

  • by modifying the list associated to each key, you should see the modification also in the lists associated to other keys

  • your code must work for any string s

Example - given:

s = 'ZOOM'

After your code, it should result:

>>> print(zd)
{'a': ['Z', 'O', 'O', 'M']
 'b': ['Z', 'O', 'O', 'M'],
 'c': ['Z', 'O', 'O', 'M'],
}
>>> zd['a'][0] = 'D'
>>> print(zd)
{'a': ['D', 'O', 'O', 'M']
 'b': ['D', 'O', 'O', 'M'],
 'c': ['D', 'O', 'O', 'M'],
}
Show solution
[55]:
s = 'ZOOM'

# write here


{'c': ['Z', 'O', 'O', 'M'], 'a': ['Z', 'O', 'O', 'M'], 'b': ['Z', 'O', 'O', 'M']}
{'c': ['D', 'O', 'O', 'M'], 'a': ['D', 'O', 'O', 'M'], 'b': ['D', 'O', 'O', 'M']}

Continue

Go on reading Dictionaries 3

[ ]: