Class #4: Tuples and dictionaries

  • Tuples
  • Dictionary methods
  • Iterating
  • Copying (Dictionary comprehensions)
  • Nested structures
  • A list of dictionaries
  • A dictionary of lists

A little note about the list API

In [1]:
sorted(['f', 'q', '1'])
Out[1]:
['1', 'f', 'q']
In [2]:
['1', 'f', 'q'].sort()
In [3]:
a = ['1', 'f', 'q']
a.sort(reverse=True)
a
Out[3]:
['q', 'f', '1']
In [4]:
print(sorted(a))
print(a.sort())
['1', 'f', 'q']
None
In [5]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
In [6]:
help(list.sort)
Help on method_descriptor:

sort(self, /, *, key=None, reverse=False)
    Stable sort *IN PLACE*.

In [7]:
help(sorted)
Help on built-in function sorted in module builtins:

sorted(iterable, /, *, key=None, reverse=False)
    Return a new list containing all items from the iterable in ascending order.
    
    A custom key function can be supplied to customize the sort order, and the
    reverse flag can be set to request the result in descending order.

In [8]:
words = "This is just a simple sentence that I'm coming up with on the fly".split()
words
Out[8]:
['This',
 'is',
 'just',
 'a',
 'simple',
 'sentence',
 'that',
 "I'm",
 'coming',
 'up',
 'with',
 'on',
 'the',
 'fly']
In [9]:
sorted(words)
Out[9]:
["I'm",
 'This',
 'a',
 'coming',
 'fly',
 'is',
 'just',
 'on',
 'sentence',
 'simple',
 'that',
 'the',
 'up',
 'with']
In [10]:
sorted(words, key=len)
Out[10]:
['a',
 'is',
 'up',
 'on',
 "I'm",
 'the',
 'fly',
 'This',
 'just',
 'that',
 'with',
 'simple',
 'coming',
 'sentence']

Tuple

In [11]:
t = (21, "hello")
type(t)
Out[11]:
tuple
In [12]:
len(t)
Out[12]:
2
In [20]:
# Tuples are immutable! The one big difference from lists:
dir(t)
Out[20]:
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'count',
 'index']
In [14]:
print(t)
(21, 'hello')
In [15]:
print(a)
['1', 'f', 'q']
In [16]:
t + a
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-16-aea9ddd4ff9a> in <module>
----> 1 t + a

TypeError: can only concatenate tuple (not "list") to tuple
In [17]:
a + t
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-13b1ffe6c7b7> in <module>
----> 1 a + t

TypeError: can only concatenate list (not "tuple") to list
In [18]:
a.append(t)
In [19]:
a
Out[19]:
['1', 'f', 'q', (21, 'hello')]
In [21]:
dog = (10, 'Phoebe')
In [22]:
dog
Out[22]:
(10, 'Phoebe')
In [23]:
point_1 = (123, -10)
In [24]:
point_1
Out[24]:
(123, -10)
In [25]:
point_1[0]
Out[25]:
123
In [26]:
# Domain language vs. Implementation language
In [27]:
help(list)
Help on class list in module builtins:

class list(object)
 |  list(iterable=(), /)
 |  
 |  Built-in mutable sequence.
 |  
 |  If no argument is given, the constructor creates a new empty list.
 |  The argument must be an iterable if specified.
 |  
 |  Methods defined here:
 |  
 |  __add__(self, value, /)
 |      Return self+value.
 |  
 |  __contains__(self, key, /)
 |      Return key in self.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iadd__(self, value, /)
 |      Implement self+=value.
 |  
 |  __imul__(self, value, /)
 |      Implement self*=value.
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __mul__(self, value, /)
 |      Return self*value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __reversed__(self, /)
 |      Return a reverse iterator over the list.
 |  
 |  __rmul__(self, value, /)
 |      Return value*self.
 |  
 |  __setitem__(self, key, value, /)
 |      Set self[key] to value.
 |  
 |  __sizeof__(self, /)
 |      Return the size of the list in memory, in bytes.
 |  
 |  append(self, object, /)
 |      Append object to the end of the list.
 |  
 |  clear(self, /)
 |      Remove all items from list.
 |  
 |  copy(self, /)
 |      Return a shallow copy of the list.
 |  
 |  count(self, value, /)
 |      Return number of occurrences of value.
 |  
 |  extend(self, iterable, /)
 |      Extend list by appending elements from the iterable.
 |  
 |  index(self, value, start=0, stop=9223372036854775807, /)
 |      Return first index of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  insert(self, index, object, /)
 |      Insert object before index.
 |  
 |  pop(self, index=-1, /)
 |      Remove and return item at index (default last).
 |      
 |      Raises IndexError if list is empty or index is out of range.
 |  
 |  remove(self, value, /)
 |      Remove first occurrence of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  reverse(self, /)
 |      Reverse *IN PLACE*.
 |  
 |  sort(self, /, *, key=None, reverse=False)
 |      Stable sort *IN PLACE*.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None

In [28]:
pgo_stack = ['main screen']
In [29]:
pgo_stack
Out[29]:
['main screen']
In [48]:
# Push
pgo_stack.append('user profile')
pgo_stack
Out[48]:
['main screen', 'user profile', 'user profile']
In [31]:
pgo_stack.append('buddy screen')
pgo_stack
Out[31]:
['main screen', 'user profile', 'buddy screen']
In [32]:
help(pgo_stack.pop)
Help on built-in function pop:

pop(index=-1, /) method of builtins.list instance
    Remove and return item at index (default last).
    
    Raises IndexError if list is empty or index is out of range.

In [49]:
# Pop
pgo_stack.pop()
Out[49]:
'user profile'
In [34]:
pgo_stack
Out[34]:
['main screen', 'user profile']
In [35]:
wait_list = []
wait_list
Out[35]:
[]
In [36]:
help(list)
Help on class list in module builtins:

class list(object)
 |  list(iterable=(), /)
 |  
 |  Built-in mutable sequence.
 |  
 |  If no argument is given, the constructor creates a new empty list.
 |  The argument must be an iterable if specified.
 |  
 |  Methods defined here:
 |  
 |  __add__(self, value, /)
 |      Return self+value.
 |  
 |  __contains__(self, key, /)
 |      Return key in self.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iadd__(self, value, /)
 |      Implement self+=value.
 |  
 |  __imul__(self, value, /)
 |      Implement self*=value.
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __mul__(self, value, /)
 |      Return self*value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __reversed__(self, /)
 |      Return a reverse iterator over the list.
 |  
 |  __rmul__(self, value, /)
 |      Return value*self.
 |  
 |  __setitem__(self, key, value, /)
 |      Set self[key] to value.
 |  
 |  __sizeof__(self, /)
 |      Return the size of the list in memory, in bytes.
 |  
 |  append(self, object, /)
 |      Append object to the end of the list.
 |  
 |  clear(self, /)
 |      Remove all items from list.
 |  
 |  copy(self, /)
 |      Return a shallow copy of the list.
 |  
 |  count(self, value, /)
 |      Return number of occurrences of value.
 |  
 |  extend(self, iterable, /)
 |      Extend list by appending elements from the iterable.
 |  
 |  index(self, value, start=0, stop=9223372036854775807, /)
 |      Return first index of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  insert(self, index, object, /)
 |      Insert object before index.
 |  
 |  pop(self, index=-1, /)
 |      Remove and return item at index (default last).
 |      
 |      Raises IndexError if list is empty or index is out of range.
 |  
 |  remove(self, value, /)
 |      Remove first occurrence of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  reverse(self, /)
 |      Reverse *IN PLACE*.
 |  
 |  sort(self, /, *, key=None, reverse=False)
 |      Stable sort *IN PLACE*.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None

In [37]:
wait_list.insert('Robb', 0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-37-49c5d0f1727d> in <module>
----> 1 wait_list.insert('Robb', 0)

TypeError: 'str' object cannot be interpreted as an integer
In [38]:
wait_list.insert(0, 'Robb')
In [39]:
wait_list
Out[39]:
['Robb']
In [46]:
# Enqueue
wait_list.insert(0, 'Cameron')
In [41]:
wait_list
Out[41]:
['Cameron', 'Robb']
In [42]:
wait_list.insert(0, 'Giovanni')
In [43]:
wait_list
Out[43]:
['Giovanni', 'Cameron', 'Robb']
In [47]:
# Dequeue
wait_list.pop()
Out[47]:
'Cameron'
In [45]:
wait_list
Out[45]:
['Giovanni', 'Cameron']

Dictionaries

In [53]:
dog_names = "phoebe maru penny".split()
dog_ages  = [10, 9, 4]

print(dog_ages)
print(dog_names)
[10, 9, 4]
['phoebe', 'maru', 'penny']
In [52]:
for i in range(len(dog_names)):
    print(dog_names[i], dog_ages[i])
phoebe 10
maru 9
penny 4
In [55]:
b = ['phoebe',10,'maru',9,'penny',4]
b
Out[55]:
['phoebe', 10, 'maru', 9, 'penny', 4]
In [56]:
dogs = {
    'phoebe' : 10,
    'maru' : 9,
    'penny' : 4,
}
dogs
Out[56]:
{'phoebe': 10, 'maru': 9, 'penny': 4}
In [57]:
dogs['maru']
Out[57]:
9
In [58]:
dogs
Out[58]:
{'phoebe': 10, 'maru': 9, 'penny': 4}
In [61]:
dogs = {
    'phoebe' : (10, 65),
    'maru' : (9, 60),
    'penny' : (4, 50),
}

dogs['robb'] = (190, 10)

dogs
Out[61]:
{'phoebe': (10, 65), 'maru': (9, 60), 'penny': (4, 50), 'robb': (190, 10)}
In [60]:
type(dogs)
Out[60]:
dict
In [62]:
dogs[maru]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-62-9be4e720cafe> in <module>
----> 1 dogs[maru]

NameError: name 'maru' is not defined
In [63]:
maru
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-63-c4de1ce7aefe> in <module>
----> 1 maru

NameError: name 'maru' is not defined
In [64]:
x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-64-6fcf9dfbd479> in <module>
----> 1 x

NameError: name 'x' is not defined
In [65]:
'x'
Out[65]:
'x'
In [66]:
help(dict)
Help on class dict in module builtins:

class dict(object)
 |  dict() -> new empty dictionary
 |  dict(mapping) -> new dictionary initialized from a mapping object's
 |      (key, value) pairs
 |  dict(iterable) -> new dictionary initialized as if via:
 |      d = {}
 |      for k, v in iterable:
 |          d[k] = v
 |  dict(**kwargs) -> new dictionary initialized with the name=value pairs
 |      in the keyword argument list.  For example:  dict(one=1, two=2)
 |  
 |  Methods defined here:
 |  
 |  __contains__(self, key, /)
 |      True if the dictionary has the specified key, else False.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __setitem__(self, key, value, /)
 |      Set self[key] to value.
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |  
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |  
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |  
 |  get(self, key, default=None, /)
 |      Return the value for key if key is in the dictionary, else default.
 |  
 |  items(...)
 |      D.items() -> a set-like object providing a view on D's items
 |  
 |  keys(...)
 |      D.keys() -> a set-like object providing a view on D's keys
 |  
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |  
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |  
 |  setdefault(self, key, default=None, /)
 |      Insert key with a value of default if key is not in the dictionary.
 |      
 |      Return the value for key if key is in the dictionary, else default.
 |  
 |  update(...)
 |      D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
 |      If E is present and has a .keys() method, then does:  for k in E: D[k] = E[k]
 |      If E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v
 |      In either case, this is followed by: for k in F:  D[k] = F[k]
 |  
 |  values(...)
 |      D.values() -> an object providing a view on D's values
 |  
 |  ----------------------------------------------------------------------
 |  Class methods defined here:
 |  
 |  fromkeys(iterable, value=None, /) from builtins.type
 |      Create a new dictionary with keys from iterable and values set to value.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None

In [67]:
dogs
Out[67]:
{'phoebe': (10, 65), 'maru': (9, 60), 'penny': (4, 50), 'robb': (190, 10)}
In [68]:
dogs['phoebe'] = 'out of town'
In [69]:
dogs
Out[69]:
{'phoebe': 'out of town', 'maru': (9, 60), 'penny': (4, 50), 'robb': (190, 10)}
In [70]:
dogs['maru'] = 'out of town'
dogs
Out[70]:
{'phoebe': 'out of town',
 'maru': 'out of town',
 'penny': (4, 50),
 'robb': (190, 10)}
In [71]:
[ name for name in dogs.keys() if dogs[name] == 'out of town' ]
Out[71]:
['phoebe', 'maru']
In [72]:
dogs.keys()
Out[72]:
dict_keys(['phoebe', 'maru', 'penny', 'robb'])
In [73]:
dogs.items()
Out[73]:
dict_items([('phoebe', 'out of town'), ('maru', 'out of town'), ('penny', (4, 50)), ('robb', (190, 10))])
In [74]:
dogs.values()
Out[74]:
dict_values(['out of town', 'out of town', (4, 50), (190, 10)])
In [75]:
[ name for (name, val) in dogs.items() if val == 'out of town' ]
Out[75]:
['phoebe', 'maru']
In [76]:
[ name for (name, val) in dogs.items() if val != 'out of town' ]
Out[76]:
['penny', 'robb']
In [77]:
for name in dogs:
    print(name)
phoebe
maru
penny
robb
In [84]:
# Which do you feel is cleaner?
# There's no right answer!
# Option 1:
for name in dogs:
    print(f"The data we have for {name} is: {dogs[name]}")
The data we have for phoebe is: out of town
The data we have for maru is: out of town
The data we have for penny is: (4, 50)
The data we have for robb is: (190, 10)
In [79]:
type(dogs)
Out[79]:
dict
In [80]:
dogs
Out[80]:
{'phoebe': 'out of town',
 'maru': 'out of town',
 'penny': (4, 50),
 'robb': (190, 10)}
In [81]:
dogs.values()
Out[81]:
dict_values(['out of town', 'out of town', (4, 50), (190, 10)])
In [83]:
# Option 2:
for name, dog_info in dogs.items():
    print(f"The data we have for {name} is: {dog_info}")
The data we have for phoebe is: out of town
The data we have for maru is: out of town
The data we have for penny is: (4, 50)
The data we have for robb is: (190, 10)
In [86]:
for name, dog_info in dogs:
    print(f"The data we have for {name} is: {dog_info}")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-86-531ea5473dc1> in <module>
----> 1 for (name, dog_info) in dogs:
      2     print(f"The data we have for {name} is: {dog_info}")

ValueError: too many values to unpack (expected 2)
In [88]:
a, b, c, d = "a really short string".split()
In [89]:
a
Out[89]:
'a'
In [90]:
b
Out[90]:
'really'
In [91]:
words = "a really short string".split()
a = words[0]
b = words[1]
In [92]:
x, y, z = (4, 5, 6, 7)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-92-03d40e1166bb> in <module>
----> 1 x, y, z = (4, 5, 6, 7)

ValueError: too many values to unpack (expected 3)
In [93]:
x, y, *z = (4, 5, 6, 7)
In [94]:
print(x)
print(y)
4
5
In [95]:
z
Out[95]:
[6, 7]
In [96]:
x, y, *z = (4, 5)
In [97]:
z
Out[97]:
[]
In [98]:
dogs
Out[98]:
{'phoebe': 'out of town',
 'maru': 'out of town',
 'penny': (4, 50),
 'robb': (190, 10)}
In [100]:
name, data = list(dogs.items())[0]

print(name)
print(data)
phoebe
out of town
In [101]:
"abc" * 10
Out[101]:
'abcabcabcabcabcabcabcabcabcabc'
In [102]:
[1, 2, 3] * 10
Out[102]:
[1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3,
 1,
 2,
 3]
In [103]:
[0] * 10
Out[103]:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
In [104]:
45 * 1000
Out[104]:
45000
In [105]:
5 ** 2
Out[105]:
25
In [106]:
5 ** 3
Out[106]:
125
In [ ]: