Tuples and Dictionaries
A tuple is a sequence that can't be changed
- Lists are not Python's only container type.
- A tuple is an ordered sequence, like a list, but it is immutable: once created, it can't be changed.
- Write a tuple with round brackets instead of square brackets.
- Indexing and slicing work just as they do for lists and strings.
fruits = ('Apple', 'Banana', 'Cherry') print(fruits[1]) print(type(fruits))
Banana <class 'tuple'>
- It's the comma that makes a tuple, not the brackets, so a tuple with one element needs a trailing comma.
single = ('Apple',) not_a_tuple = ('Apple') print(type(single), type(not_a_tuple))
<class 'tuple'> <class 'str'>
- Trying to change an element raises a
TypeError:
greeting = ('Hello', 'World') greeting[0] = 'Goodbye'
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
- Use a tuple for a fixed group of values that belong together, such as a coordinate
(x, y)or a date(year, month, day). - Functions often use a tuple to return more than one value, which the caller can unpack into separate variables:
def min_max(values): return min(values), max(values) smallest, largest = min_max([3, 1, 4, 1, 5]) print(smallest, largest)
1 5
A dictionary looks values up by key
- In a list, we look up an element by its position, a number.
- In a dictionary (
dict), we look up a value by a key of our choice, often a string. - Write a dictionary with curly brackets and
key: valuepairs, separated by commas.
person = {'name': 'Ada', 'age': 36, 'jobs': ['Mathematician', 'Writer']} print(person['name']) print(person['jobs']) print(type(person))
Ada ['Mathematician', 'Writer'] <class 'dict'>
- Dictionaries are mutable: assign to a key to add a new entry or replace an existing one.
person['age'] = 37 person['country'] = 'UK' print(person)
{'name': 'Ada', 'age': 37, 'jobs': ['Mathematician', 'Writer'], 'country': 'UK'}
- Looking up a key that isn't there raises a
KeyError. You saw one in the "Reading Error Messages" challenge in Errors and Exceptions. - Use
.getto supply a default instead:
print(person.get('email', 'no email given'))
no email given
- Dictionaries remember the order in which keys were added (since Python 3.7).
Keys and values can be listed and tested separately
.keys()gives the keys and.values()gives the values..items()gives(key, value)tuples, which is the usual way to loop over a dictionary.
scores = {'Ada': 92, 'Grace': 88, 'Alan': 75} print(list(scores.keys())) print(list(scores.values())) for name, score in scores.items(): print(name, 'scored', score)
['Ada', 'Grace', 'Alan'] [92, 88, 75] Ada scored 92 Grace scored 88 Alan scored 75
intests the keys of a dictionary, not its values:
print('Ada' in scores) print(92 in scores) print(92 in scores.values())
True False True
Dictionary keys must be immutable
- Dictionaries use a technique called hashing to find keys quickly.
- This only works if a key can't change after it has been stored, so keys must be immutable: numbers, strings and tuples can be keys, but lists can't.
good_match = { ('Lamb', 'Mint'): True, ('Bacon', 'Chocolate'): False, } print(good_match[('Lamb', 'Mint')])
True
illegal = { ['Lamb', 'Mint']: True, ['Bacon', 'Chocolate']: False, }
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'
- Remember: square brackets make lists, round brackets make tuples.
Copying a container needs care
- In Lists we saw that
primes = oddsdoesn't copy a list. It gives the same list a second name. Changing the list through either name changes it for both. - This is true of every mutable container, including dictionaries.
- To copy a list, use
list(odds)or the sliceodds[:]. For a dictionary, usedict(person)orperson.copy().
x = [1, 2, 3] y = x[:] y[1] = 20 print(x, y)
[1, 2, 3] [1, 20, 3]
- These are shallow copies: they make a new outer container, but the elements inside are shared.
- That matters when the container holds other mutable containers, like a list of lists:
x = [['a', 'b'], 'c'] y = x z = x[:] x[0][1] = 'd' z[1] = 'e' print(x) print(y) print(z)
[['a', 'd'], 'c'] [['a', 'd'], 'c'] [['a', 'd'], 'e']
yis just another name forx, so it sees every change.zis a new list, so changingz[1]doesn't affectx. Butz[0]is the same inner list asx[0], so it sees the change tox[0][1].- To copy everything, however deeply nested, use
deepcopyfrom the standard library'scopymodule:
from copy import deepcopy x = [['a', 'b'], 'c'] z = deepcopy(x) x[0][1] = 'd' print(x) print(z)
[['a', 'd'], 'c'] [['a', 'b'], 'c']
- You'll learn more about
importin Libraries.
Counting Words
Fill in the blanks so that
counts records how many times each word appears in words.words = ['red', 'blue', 'red', 'green', 'blue', 'red'] counts = {} for word in words: if word ____ counts: counts[word] = ____ else: counts[word] = ____ print(counts)
{'red': 3, 'blue': 2, 'green': 1}
Copying a Grid
What does this program print, and why?
How would you change it so that
grid is not affected?grid = [[0, 0], [0, 0]] backup = grid[:] backup[0][0] = 1 backup[1] = [9, 9] print(grid)