Dictionaries 2 - operators
Download exercise zip
There are several operators to manipulate dictionaries:
Operator |
Syntax |
Return |
Description |
---|---|---|---|
|
|
Retorn the number of keys |
|
dict |
obj |
Return the value associated to the key |
|
dict |
Adds or modify the value associated to the key |
||
|
Removes the key/value couple |
||
obj |
|
Return |
|
|
|
Checks whether two dictionaries are equal or different |
What to do
Unzip exercises zip in a folder, you should obtain something like this:
dictionaries
dictionaries1.ipynb
dictionaries1-sol.ipynb
dictionaries2.ipynb
dictionaries2-sol.ipynb
dictionaries3.ipynb
dictionaries3-sol.ipynb
dictionaries4.ipynb
dictionaries4-sol.ipynb
dictionaries5-chal.ipynb
jupman.py
WARNING: to correctly visualize the notebook, it MUST be in an unzipped folder !
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
Go on reading the exercises file, sometimes you will find paragraphs marked Exercises 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):
len(dict())
len({'a':{}})
len({(1,2):{3},(4,5):{6},(7,8):{9}})
len({1:2,1:2,2:4,2:4,3:6,3:6})
len({1:2,',':3,',':4,})
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] )
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]
QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):
{'a':4,'b':5}('a')
{1:2,2:3,3:4}[2]
{'a':1,'b':2}['c']
{'a':1,'b':2}[a]
{'a':1,'b':2}[1]
{'a':1,'b':2,'c':3}['c']
{'a':1,'b':2,'c':3}[len(['a','b','c'])]
{(3,4):(1,2)}[(1,2)]
{(1,2):(3,4)}[(1,2)]
{[1,2]:[3,4]}[[1,2]]
{'a','b','c'}['a']
{'a:b','c:d'}['c']
{'a':4,'b':5}{'a'}
d1 = {'a':'b'} d2 = {'b':'c'} print(d1[d2['c']])
d1 = {'a':'b'} d2 = {'b':'c'} print(d2[d1['a']])
{}[]
{[]:3}[[]]
{1:7}['1']
{'':7}"[]"
{'':7}[""]
{"":7}['']
{'"':()}['']
{():7}[()]
{(()):7}[()]
{(()):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}
[8]:
d1 = {'a':6, 'b':2,'c':5}
# write here
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:
adding a key/value couple
associate an existing key to a different value
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]:
{'chair': 'a piece of furniture to sit on',
'cupboard': 'a cabinet for storage',
'lamp': 'a device to provide illumination',
'armchair': 'a chair with armrests'}
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]:
{'bla': 3, 4: 'boh', (7, 9): ['gar', 'bage'], 5.0: 'a float'}
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):
d = {1:'a'} d[2] = 'a' print(d)
d = {} print(len(d)) d['a'] = 'b' print(len(d))
d1 = {'a':3, 'b':4} diz2 = diz1 diz1['a'] = 5 print(diz1) print(diz2)
diz1 = {'a':3, 'b':4} diz2 = dict(diz1) diz1['a'] = 5 print(diz1) print(diz2)
la = ['a','c'] diz = {'a':3, 'b':4, 'c':5} diz['d'] = diz[la[0]] + diz[la[1]] print(diz)
diz = {} diz[()]: '' diz[('a',)]: 'A' diz[('a','b')]: 'AB' print(diz)
la = [5,8,6,9] diz = {} diz[la[0]]=la[2] diz[la[2]]=la[0] print(diz)
diz = {} diz[(4,5,6)[2]] = 'c' diz[(4,5,6)[1]] = 'b' diz[(4,5,6)[0]] = 'a' print(diz)
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
:
set the
'bolts'
key value equal to the value of the'pincers'
keyincrement the value of
wheels
key of1
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}
[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):
diz = {'a':'b'} diz['a'] = 'a' print(diz)
diz = {'1':'2'} diz[1] = diz[1] + 5 # nasty print(diz)
diz = {1:2} diz[1] = diz[1] + 5 print(diz)
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]:
{'pots': 3, 'forks': 20}
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):
diz = {'a':'b'} del diz['b'] print(diz)
diz = {'a':'b', 'c':'d'} del diz['a'] print(diz)
diz = {'a':'b', 'c':'d'} del diz['a'] del diz['a'] print(diz)
diz = {'a':'b'} new_diz = del diz['a'] print(diz) print(new_diz)
diz1 = {'a':'b', 'c':'d'} diz2 = diz1 del diz1['a'] print(diz1) print(diz2)
diz1 = {'a':'b', 'c':'d'} diz2 = dict(diz1) del diz1['a'] print(diz1) print(diz2)
diz = {'a':'b'} del diz['c'] print(diz)
diz = {'a':'b'} diz.del('a') print(diz)
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 =
[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 eliminatea 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 into_remove
andto_add
assume that
to_add
always and only containstulips
androses
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}
[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 ofhello
androad
assume that
it_es
always and only contains translations ofciao
andstrada
in the solution, ONLY use the constants
'hello'
and'road'
, you will take the others you need from the dictionariesDO 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'}
[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):
('a') in {'a':5}
('a','b') in {('a','b'):5}
('a','b',) in {('a','b'):5}
['a','b'] in {('a','b'):5}
{3: 'q' in {'q':5}}
{'q' not in {'q':0} : 'q' in {'q':0}}
{'a' in 'b'}
{'a' not in {'b':'a'}}
len({'a':6,'b':4}) in {1:2}
'ab' in {('a','b'): 'ab'}
None in {}
None in {'None':3}
None in {None:3}
not None in {0:None}
Exercise - The Helmsman
The restaurant “The Helmsman” serves a menu
with exactly 3 courses each coupled with a side dish. The courses and the side dishes are numbered from 1 to 12. There are many international clients who don’t speak well the local language, so they often simply point a course number. They never point a side dish. Once the order
is received, the waiter with a tablet verifies whether the course is ready with the correct side dish. Write some code which given an index of a course
shows True
if this is in the kitchen
coupled with the course, False
otherwise.
DO NOT use
if
DO NOT use loops nor list comprehensions
HINT: if you don’t know how to do it, look at Booleans - Evaluation order
Example 1 - given:
# 1 2 3 4 5 6
menu = ['herring','butter','orata','salad', 'salmon', 'potatoes',
# 7 8 9 10 11 12
'tuna', 'beans', 'salmon', 'lemon', 'herring', 'salad']
kitchen = {'orata':'salad',
'salmon':'potatoes',
'herring':'salad',
'tuna':'beans'}
order = 1
The program will show False
, because there is no association "herring" : "butter"
in kitchen
Example 2 - given:
order = 3
the program will show True
because there is the association "orata" : "salad"
in cambusa
[33]:
order = 1 # False
#order = 3 # True
#order = 5 # True
#order = 7 # True
#order = 9 # False
#order = 11 # True
# 1 2 3 4 5 6
menu = ['herring','butter','orata','salad', 'salmon', 'potatoes',
# 7 8 9 10 11 12
'tuna', 'beans', 'salmon', 'lemon', 'herring', 'salad']
kitchen = {'orata':'salad',
'salmon':'potatoes',
'herring':'salad',
'tuna':'beans'}
# write here
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:
[34]:
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:
[35]:
# WARNING: FOR PYTHON TUTOR TO WORK, REMEMBER TO EXECUTE THIS CELL with Shift+Enter
# (it's sufficient to execute it only once)
import jupman
[36]:
loans = {'Marco': ['Les Misérables', 'Ulysses'],
'Gloria': ['War and Peace'],
'Rita': ['The Shining','Dracula','1984']}
jupman.pytut()
[36]:
If we try writing the expression:
[37]:
loans['Rita']
[37]:
['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:
[38]:
loans['Rita'][1]
[38]:
'Dracula'
Equivalently, we might also save a pointer to the list by assigning the expression to a variable:
[39]:
ritas_list = loans['Rita']
[40]:
ritas_list
[40]:
['The Shining', 'Dracula', '1984']
[41]:
ritas_list[1]
[41]:
'Dracula'
Let’s see everything in Python Tutor:
[42]:
loans = {'Marco': ['Les Misérables', 'Ulysses'],
'Gloria': ['War and Peace'],
'Rita': ['The Shining','Dracula','1984']}
ritas_list = loans['Rita']
print(ritas_list[1])
jupman.pytut()
Dracula
[42]:
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:
The first book borrowed by Gloria (
'War and Peace'
) and the last one borrowed by Rita ('1984'
)The number of books borrowed by Rita
True
if everybody among Marco, Gloria and Rita borrowed at least a book,False
otherwise
[43]:
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
Exercise - Shark Bay
The West India Company asked you to explore the tropical seas, which are known fo rthe dangerous species which live in their waters. You are provided with a dmap
which associates places to species found therein:
dmap = {
"Shark Bay" : ["sharks"],
"Estuary of Bad Luck" : ["crocodiles", "piraña"],
"Shipwreck Trench" : ["killer whales", "tiger fishes"],
}
You are also given vague directions about how to update the dmap
, using these variables:
place = "Shipwreck Trench"
dangers = ["morays", "blue spotted octupus"]
travel = "Sunken Sails Offshore"
exploration = ["barracudas", "jellyfishes"]
Try writing some code which using the variables above (or data from the map itself) MODIFIES dmap
so to obtain:
>>> dmap
{'Shark Bay' : ['sharks'],
'Estuary of Bad Luck' : ['crocodiles', 'piraña', 'jellyfishes'],
'Shipwreck Trench' : ['killer whales', 'tiger fishes'],
'Jellyfishes Offshore': ['barracudas', 'jellyfishes', 'crocodiles', 'piraña']}
IMPORTANT: DO NOT use constant strings in your code (so no
"Shipwreck Trench"
…). Numerical constants are instead allowed.
[44]:
place = "Estuary of Bad Luck"
dangers = ["morays", "blue spotted octupus"]
travel = "Sunken Sails Offshore"
exploration = ["barracudas", "jellyfishes"]
dmap = {
"Shark Bay" : ["sharks"],
"Estuary of Bad Luck": ["crocodiles", "piraña"],
"Shipwreck Trench" : ["killer whales", "tiger fishes"],
}
# write here
Exercise - The Storm Sea
The West India Company asks you now to produce a new
map starting from dmap1
and dmap2
. The new
map must contain all the items from dmap1
, expanded with the items from place1
and place2
.
assume the items
place1
andplace2
are always present indmap1
anddmap2
.IMPORTANT: the execution of your code must not change
dmap1
nordmap2
Example - given:
dmap1 = {
"Shark Bay" : ["sharks"],
"Estuary of Bad Luck" : ["crocodiles", "piraña"],
"Storm Sea" : ["barracudas", "morays"]
}
dmap2 = {
"Estuary of Bad Luck" : ["morays", "shark fishes"],
"Storm Sea" : ["giant octupses"],
"Shipwreck Trench" : ["killer whales"],
"Lake of the Hopeless" : ["water vortexes"]
}
place1, place2 = "Estuary of Bad Luck", "Storm Sea"
After your code, it must result:
>>> new
{'Estuary of Bad Luck': ['crocodiles', 'piraña', 'morays', 'shark fishes'],
'Shark Bay': ['sharks'],
'Storm Sea': ['barracudas', 'morays', 'giant octupses']}
>>> dmap1 # not changed
{'Estuary of Bad Luck': ['crocodiles', 'piraña'],
'Shark Bay': ['sharks'],
'Storm Sea': ['barracudas', 'morays']}
>>> dmap2 # not changed
{'Estuary of Bad Luck': ['morays', 'shark fishes'],
'Lake of the Hopeless': ['water vortexes'],
'Shipwreck Trench': ['killer whales'],
'Storm Sea': ['giant octupses']}
[45]:
dmap1 = {
"Shark Bay" : ["sharks"],
"Estuary of Bad Luck" : ["crocodiles", "piraña"],
"Storm Sea" : ["barracudas", "morays"]
}
dmap2 = {
"Estuary of Bad Luck" : ["morays", "shark fishes"],
"Storm Sea" : ["giant octupses"],
"Shipwreck Trench" : ["killer whales"],
"Lake of the Hopeless" : ["water vortexes"]
}
place1, place2 = "Estuary of Bad Luck", "Storm Sea"
# write here
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:
[46]:
{'a':3, 'b':4} == {'a':3, 'b':4}
[46]:
True
[47]:
{'a':3, 'b':4} == {'c':3, 'b':4}
[47]:
False
[48]:
{'a':3, 'b':4} == {'a':3, 'b':999}
[48]:
False
We can verify equality of dictionaries with a different number of elements:
[49]:
{'a':3, 'b':4} == {'a':3}
[49]:
False
[50]:
{'a':3, 'b':4} == {'a':3,'b':3,'c':5}
[50]:
False
… and with heterogenous elements:
[51]:
{'a':3, 'b':4} == {2:('q','p'), 'b':[99,77]}
[51]:
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:
[52]:
{'a':5, 'b':7} == {'b':7, 'a':5}
[52]:
True
What about incremental update?
[53]:
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:2} == {2:1}
{1:2,3:4} == {3:4,1:2}
{'a'.upper():3} == {'a':3}
{'A'.lower():3} == {'a':3}
{'a': {1:2} == {3:4}}
diz1 = {} diz1[2] = 5 diz1[3] = 7 diz2 = {} diz2[3] = 7 diz2[2] = 5 print(diz1 == diz2)
diz1 = {'a':3,'b':8} diz2 = diz1 diz1['a'] = 7 print(diz1 == diz2)
diz1 = {} diz1['a']=3 diz2 = diz1 diz2['a']=4 print(diz1 == diz2)
diz1 = {'a':3, 'b':4, 'c':5} diz2 = {'a':3,'c':5} del diz1['a'] print(diz1 == diz2)
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.
[54]:
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}
[54]:
QUESTION: If you execute the following code in Python Tutor, what will it print?
Which type of copy did we do? Shallow? Deep? (or both …?)
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)
[55]:
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}
[55]:
QUESTION: If you execute the following code in Python Tutor, what will it print?
Which type of copy did we do? Shallow? Deep? (or both …?)
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)
[56]:
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]}
[56]:
QUESTION: If you execute the following code in Python Tutor, what will it print?
Which type of copy did we do? Shallow? Deep? (or both …?)
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)
[57]:
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]}
[57]:
QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):
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)
da = {'a':['x','y','z']} db = dict(da) db['a'] = ['w','t'] dc = dict(db) print(da) print(db) print(dc)
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'],
}
[58]:
s = 'ZOOM'
# write here
Continue
Go on reading Dictionaries 3 - methods
[ ]: